File Hub Client (tamar-file-hub-client)
file-hub-server 的官方 Python SDK。基于 gRPC 通信,提供异步与同步两套客户端,覆盖文件管理、文件夹、文档、媒体压缩变体、协同电子表格 (Taple) 全部接口。
服务端通过 gRPC 调度 file-hub-executor 完成压缩与跨境同步;本 SDK 只与服务端直接通信。
目录
安装
pip install tamar-file-hub-client
支持 Python 3.8 ~ 3.12。包内含 py.typed,完整类型注解。
依赖:grpcio、grpcio-tools、protobuf==5.27.2、pydantic>=2、requests、aiohttp、aiofiles、python-magic(Windows: python-magic-bin)。
快速开始
同步客户端
from file_hub_client import TamarFileHubClient, Role
with TamarFileHubClient(
host="grpc.example.com",
port=50051,
secure=True,
credentials={"api_key": "your-api-key"},
) as client:
client.set_user_context(
org_id="org-123",
user_id="user-456",
role=Role.ACCOUNT,
)
result = client.blobs.upload(file=b"hello world")
print(result.file.id, result.upload_file.file_size)
异步客户端
import asyncio
from file_hub_client import AsyncTamarFileHubClient, Role
async def main():
async with AsyncTamarFileHubClient(host="grpc.example.com") as client:
client.set_user_context(org_id="org-123", user_id="user-456", role=Role.ACCOUNT)
files = await client.files.list_files(folder_id="folder-1", page_size=20)
for f in files.files:
print(f.id, f.file_name)
asyncio.run(main())
两套客户端方法签名一致,按运行环境选用。也可通过 get_client() / get_async_client() 获取单例。
客户端构造
TamarFileHubClient(
host="grpc.example.com", # 或 FILE_HUB_HOST
port=50051, # 或 FILE_HUB_PORT(可省略)
secure=True, # 或 FILE_HUB_SECURE
credentials={"api_key": "..."}, # 或 FILE_HUB_API_KEY
auto_connect=True,
retry_count=3,
retry_delay=1.0,
user_context=UserContext(...),
request_context=RequestContext(...),
enable_logging=True,
log_level="INFO",
options=[
("grpc.max_send_message_length", 100 * 1024 * 1024),
("grpc.max_receive_message_length", 100 * 1024 * 1024),
],
)
环境变量
构造参数均可由环境变量提供,构造时显式传参的优先级高于环境变量。便于 12-factor 部署:
| 变量 | 对应参数 | 默认值 | 说明 |
|---|---|---|---|
FILE_HUB_HOST |
host |
localhost |
gRPC 服务器地址,可以是域名或 IP |
FILE_HUB_PORT |
port |
(空) | gRPC 端口;留空时直接以 host 作为完整地址(线上走域名常这样用) |
FILE_HUB_SECURE |
secure |
false |
是否启用 TLS,取值 true / false |
FILE_HUB_API_KEY |
credentials |
(空) | API Key,存在时自动转为 {"api_key": ...} |
FILE_HUB_RETRY_COUNT |
retry_count |
3 |
channel 创建失败时的重试次数;网络连接由首次 RPC 懒建立 |
FILE_HUB_RETRY_DELAY |
retry_delay |
1.0 |
channel 创建重试间隔(秒) |
# 本地开发
export FILE_HUB_HOST=localhost
export FILE_HUB_PORT=50051
export FILE_HUB_SECURE=false
# 生产(域名 + TLS,无需指定端口)
export FILE_HUB_HOST=api.filehub.example.com
export FILE_HUB_SECURE=true
export FILE_HUB_API_KEY=prod-api-key-xxxxx
# 全部走环境变量,无需传参
with TamarFileHubClient() as client:
client.set_user_context(org_id="org-123", user_id="user-456")
...
UserContext(org_id / user_id / role / actor_id / user_ip)需要通过set_user_context()显式设置,不从环境变量读取——它属于业务上下文而非连接配置。完整模板见仓库.env.example。
用户上下文
client.set_user_context(
org_id="org-123",
user_id="user-456",
role=Role.ACCOUNT, # ACCOUNT | AGENT | SYSTEM
actor_id="operator-id", # 可选,操作者
user_ip="192.168.1.1", # 可选,审计日志
)
上下文随每次 gRPC 调用透传到服务端,用于资源归属、权限校验、操作审计。
API 导航
共 96 个公开方法,分布在 5 个服务中。所有方法的 request_id 和 **metadata 参数均为可选,用于链路追踪和 gRPC metadata 注入,下文参数表中省略。
BlobService (client.blobs) — 26 个方法
| 方法 | 说明 |
|---|---|
upload |
上传文件(bytes / Path / URL) |
generate_upload_url |
获取预签名上传 URL |
generate_resumable_upload_url |
获取 GCS 专属断点续传 URL |
upload_oss_multipart |
OSS multipart 直传并支持断点恢复 |
create_oss_multipart_upload |
创建 OSS multipart upload |
sign_oss_upload_part |
签发 OSS 分片上传 URL |
list_oss_uploaded_parts |
从 OSS 查询已上传分片 |
complete_oss_multipart_upload |
完成 OSS multipart upload 并触发服务端校验 |
abort_oss_multipart_upload |
取消 OSS multipart upload |
confirm_upload_completed |
确认上传完成 |
download |
下载文件到本地 |
download_to_bytes |
下载文件到内存 |
generate_download_url |
获取下载 URL |
batch_generate_download_url |
批量获取下载 URL |
create_download_task |
创建单文件 / 批量归档下载任务 |
get_download_task_status |
查询下载任务状态 |
generate_download_task_url |
获取已完成下载任务的下载 URL |
run_download_task_dispatch_sweep |
触发下载任务投递补偿扫描 |
run_download_task_reconcile_sweep |
触发下载任务结果对账扫描 |
get_gcs_url |
获取 GCS 对象 URL |
batch_get_gcs_url |
批量获取 GCS 对象 URL |
get_compression_status |
查询压缩状态 |
get_compressed_variants |
获取压缩变体列表 |
trigger_recompression |
触发重新压缩 |
generate_variant_download_url |
获取变体下载 URL |
batch_get_file_status |
批量查询文件状态(上传/压缩/同步) |
FileService (client.files) — 13 个方法
| 方法 | 说明 |
|---|---|
get_file |
获取文件详情 |
get_files |
批量获取文件详情 |
list_files |
分页列表文件 |
rename_file |
重命名文件 |
delete_file |
删除文件 |
generate_share_link |
创建分享链接 |
visit_file |
记录文件访问 |
get_compression_status |
查询压缩状态 |
get_compressed_variants |
获取压缩变体列表 |
trigger_recompression |
触发重新压缩 |
generate_variant_download_url |
获取变体下载 URL |
import_from_gcs |
从 GCS 导入文件 |
generate_signed_gcs_url |
生成 GCS 签名 URL |
FolderService (client.folders) — 9 个方法
| 方法 | 说明 |
|---|---|
create_folder |
创建文件夹 |
get_folder |
查询单个文件夹 |
get_or_create_default_folder |
查询或创建默认根文件夹 |
rename_folder |
重命名文件夹 |
move_folder |
移动文件夹 |
update_folder |
同时更新文件夹名称和/或父文件夹 |
delete_folder |
删除文件夹 |
list_folders |
列出文件夹 |
list_folder_tree |
以树形结构列出文件夹 |
DocumentService (client.documents) — 12 个方法
| 方法 | 说明 |
|---|---|
create_document |
创建文档及初始内容 |
get_document |
按 document_id 或 content_id 获取文档 |
update_document |
更新文档内容和标题 |
delete_document |
删除文档 |
search_documents |
搜索可见文档 |
list_owner_documents |
列出用户拥有的文档 |
list_recently_visited_documents |
列出最近访问文档 |
share_document |
分享文档权限 |
clone_document |
克隆文档到目标 org/user |
append_document_content |
追加文档内容 |
update_document_content |
更新指定 content 节点 |
add_document_agent_operations |
批量创建 agent operation |
TapleService (client.taples) — 36 个方法
表操作
| 方法 | 说明 |
|---|---|
create_table |
创建表 |
get_table |
获取表详情(table_id 或 file_id) |
update_table |
更新表名/描述 |
delete_table |
删除表 |
Sheet 操作
| 方法 | 说明 |
|---|---|
create_sheet |
创建 Sheet |
get_sheet |
获取 Sheet 详情 |
list_sheets |
列出表内所有 Sheet |
update_sheet |
更新 Sheet 名/描述/位置 |
delete_sheet |
删除 Sheet |
get_sheet_version |
获取 Sheet 当前版本号 |
get_sheet_data |
获取 Sheet 完整数据 |
batch_edit_sheet |
批量混合编辑(行/列/单元格) |
列操作
| 方法 | 说明 |
|---|---|
create_column |
创建列 |
update_column |
更新列属性 |
delete_column |
删除列 |
batch_edit_columns |
批量列操作 |
get_column_data |
获取列数据 |
行操作
| 方法 | 说明 |
|---|---|
create_row |
创建行 |
update_row |
更新行属性 |
delete_row |
删除行 |
batch_edit_rows |
批量行操作 |
get_row_data |
获取行数据 |
单元格操作
| 方法 | 说明 |
|---|---|
edit_cell |
编辑单元格 |
delete_cell |
清空单元格 |
batch_edit_cells |
批量单元格操作 |
get_cell_data |
获取单元格数据 |
视图操作
| 方法 | 说明 |
|---|---|
create_table_view |
创建视图 |
get_table_view |
获取视图 |
list_table_views |
列出视图 |
update_table_view |
更新视图 |
update_table_view_config |
更新视图配置 |
delete_table_view |
删除视图 |
batch_create_table_views |
批量创建视图 |
导入导出
| 方法 | 说明 |
|---|---|
clone_table_data |
克隆表到另一用户/组织 |
export_table_data |
导出表数据 |
import_table_data |
导入数据到表 |
API 参考
以下示例均使用同步客户端。异步客户端方法签名相同,调用时加
await即可。
BlobService — 文件传输与变体
blob.upload
上传文件,支持 bytes / 文件路径 / URL 三种输入。
upload(
file: Union[str, Path, BinaryIO, bytes] = None,
*,
folder_id: str = None,
mode: UploadMode = UploadMode.NORMAL, # NORMAL | RESUMABLE | STREAM | OSS_MULTIPART
is_temporary: bool = False,
expire_seconds: int = None,
keep_original_filename: bool = False,
forbid_overwrite: bool = True,
url: str = None, # 从 URL 拉取上传
file_name: str = None,
mime_type: str = None, # 显式 MIME,覆盖自动探测
part_size: int = None, # OSS multipart 分片大小
oss_upload_id: str = None, # OSS multipart 恢复用 upload_id
oss_file_id: str = None, # OSS multipart 恢复用 file_id
abort_on_error: bool = False, # OSS multipart 失败时是否自动取消会话
) → FileUploadResponse
from pathlib import Path
from file_hub_client import UploadMode
# 上传字节
result = client.blobs.upload(file=b"hello world")
# 上传本地文件
result = client.blobs.upload(file=Path("/data/photo.jpg"), keep_original_filename=True)
# GCS 断点续传模式
result = client.blobs.upload(file=Path("/data/large.mp4"), mode=UploadMode.RESUMABLE)
# OSS multipart 断点续传模式
result = client.blobs.upload(
file=Path("/data/large.zip"),
mode=UploadMode.OSS_MULTIPART,
part_size=8 * 1024 * 1024,
)
# 从 URL 拉取
result = client.blobs.upload(url="https://example.com/data.csv", file_name="data.csv")
# 临时文件(自动过期)
result = client.blobs.upload(file=b"tmp", is_temporary=True, expire_seconds=3600)
blob.generate_upload_url
创建统一上传入口。默认返回普通桶直传预签名 PUT URL;设置 is_resumable_mode=True 时,由 Server 根据客户端 IP/区域策略选择 GCS resumable 或 OSS multipart,调用方只需要根据响应里的 storage_type 和 upload_strategy 执行后续上传流程。
generate_upload_url(
file_name: str,
file_size: int,
folder_id: str = None,
file_type: str = "dat",
mime_type: str = None,
file_hash: str = None,
is_temporary: bool = False,
expire_seconds: int = None,
keep_original_filename: bool = False,
forbid_overwrite: bool = True,
is_resumable_mode: bool = False,
part_size: int = None,
) → UploadUrlResponse
resp = client.blobs.generate_upload_url(file_name="report.pdf", file_size=1024 * 1024)
print(resp.upload_url) # 预签名 PUT URL
print(resp.file.id) # 已创建的文件 ID
# 自行 PUT 时必须原样透传服务端返回的 headers。
import requests
with open("report.pdf", "rb") as file_obj:
requests.put(resp.upload_url, data=file_obj, headers=resp.headers)
print(resp.storage_type) # gcs / oss
print(resp.upload_strategy) # single_put / already_uploaded
print(resp.headers) # PUT 时需要携带的 header
resp = client.blobs.generate_upload_url(
file_name="video.mp4",
file_size=5_000_000_000,
is_resumable_mode=True,
part_size=8 * 1024 * 1024,
)
print(resp.storage_type) # gcs / oss,由 Server 决定
print(resp.upload_strategy) # gcs_resumable / oss_multipart / already_uploaded
print(resp.upload_id) # OSS multipart 时返回
print(resp.part_size) # OSS multipart 时返回
blob.generate_resumable_upload_url
获取 GCS 专用断点续传 session URL,适用于明确目标为 GCS 的内部调用。OSS 断点续传请使用 generate_upload_url(is_resumable_mode=True) 返回的 OSS multipart 字段。
generate_resumable_upload_url(
file_name: str,
file_size: int,
folder_id: str = None,
file_type: str = "dat",
mime_type: str = None,
file_hash: str = None,
is_temporary: bool = False,
expire_seconds: int = None,
keep_original_filename: bool = False,
forbid_overwrite: bool = False,
) → UploadUrlResponse
返回的 upload_url 已经是 GCS session URL,不需要再调用 start_resumable_session;自行上传时同样必须原样透传 resp.headers。
blob.confirm_upload_completed
通知服务端客户端已完成上传(用于预签名 / 断点续传场景)。
blob.upload_oss_multipart
通过 OSS multipart 直传文件。SDK 会创建 multipart upload、签发分片 URL、跳过 OSS 已存在且大小匹配的分片,并在完成后调用 Server 校验最终对象。
默认情况下,分片上传失败不会自动 abort OSS multipart session;SDK 会抛出 UploadError,并在异常中携带 file_id、upload_id、part_number,方便客户端保存断点后继续恢复上传。如果业务希望一次性上传失败后立即放弃本次会话,可以传入 abort_on_error=True,SDK 会在抛出异常前尝试调用 abort_oss_multipart_upload()。
upload_oss_multipart(
file: Union[str, Path, BinaryIO, bytes],
*,
folder_id: str = None,
is_temporary: bool = False,
expire_seconds: int = None,
keep_original_filename: bool = False,
forbid_overwrite: bool = True,
file_name: str = None,
mime_type: str = None,
part_size: int = None,
upload_id: str = None,
file_id: str = None,
abort_on_error: bool = False,
) → FileUploadResponse
from pathlib import Path
# 新建 OSS multipart 上传
result = client.blobs.upload_oss_multipart(
file=Path("/data/archive.zip"),
part_size=8 * 1024 * 1024,
)
# 恢复已有上传;file_id/upload_id/part_size 应由客户端 checkpoint 保存
result = client.blobs.upload_oss_multipart(
file=Path("/data/archive.zip"),
file_id="file-123",
upload_id="oss-upload-id",
part_size=8 * 1024 * 1024,
)
OSS multipart 低阶接口
当业务方需要自己控制分片上传、保存 checkpoint 或做跨进程恢复时,可以直接调用低阶接口。
create_resp = client.blobs.create_oss_multipart_upload(
file_name="archive.zip",
file_size=512 * 1024 * 1024,
part_size=8 * 1024 * 1024,
)
sign_resp = client.blobs.sign_oss_upload_part(
file_id=create_resp.file.id,
upload_id=create_resp.upload_id,
part_numbers=[1, 2, 3],
)
parts_resp = client.blobs.list_oss_uploaded_parts(
file_id=create_resp.file.id,
upload_id=create_resp.upload_id,
)
result = client.blobs.complete_oss_multipart_upload(
file_id=create_resp.file.id,
upload_id=create_resp.upload_id,
parts=parts_resp.parts,
)
client.blobs.abort_oss_multipart_upload(
file_id=create_resp.file.id,
upload_id=create_resp.upload_id,
)
恢复上传时,客户端应保存 file_id、upload_id、part_size 和已上传分片 ETag。恢复后以 list_oss_uploaded_parts() 返回的 OSS 远端结果为准,缺失或大小不匹配的分片重新上传。Server 不维护 multipart session 表。
blob.confirm_upload_completed
通知 Server 客户端已完成上传。Server 会根据持久化上传记录判断策略:预签名 / GCS 断点续传只校验对象是否真实存在且大小匹配;OSS multipart 需要同时传入 upload_id 和 parts,由 Server 先完成 OSS 原生 multipart session 再校验对象。
confirm_upload_completed(
file_id: str,
upload_id: str | None = None,
parts: list[OssUploadedPart | dict] | None = None,
) → None
url_resp = client.blobs.generate_resumable_upload_url(file_name="big.zip", file_size=5_000_000_000)
# ... 客户端自行上传到 url_resp.upload_url ...
client.blobs.confirm_upload_completed(file_id=url_resp.upload_file.id)
blob.download
下载文件到本地路径。
download(
file_id: str,
*,
save_path: Union[str, Path] = None,
chunk_size: int = None,
) → Union[bytes, Path]
client.blobs.download(file_id="file-123", save_path="/local/output.bin")
blob.download_to_bytes
下载文件内容到内存。
download_to_bytes(file_id: str) → bytes
content = client.blobs.download_to_bytes(file_id="file-123")
blob.generate_download_url
获取预签名下载 URL。
generate_download_url(
file_id: str,
*,
is_cdn: bool = True,
expire_seconds: int = None,
network_preference: str = None,
) → str
url = client.blobs.generate_download_url(file_id="file-123", expire_seconds=3600)
blob.batch_generate_download_url
批量获取下载 URL。
batch_generate_download_url(
file_ids: List[str],
*,
is_cdn: bool = True,
expire_seconds: int = None,
network_preference: str = None,
) → BatchDownloadUrlResponse
blob.create_download_task
创建下载任务。单个文件会生成 direct 任务;多个文件会生成 archive 任务,由服务端异步投递给 Executor 打包,完成后再通过任务 URL 下载。
create_download_task(
file_items: List[Union[CreateDownloadTaskFileItem, str, dict]],
*,
download_name: str = None,
business_refs: List[Union[DownloadTaskBusinessRef, dict]] = None,
) → CreateDownloadTaskResponse
from file_hub_client import CreateDownloadTaskFileItem, DownloadTaskBusinessRef
resp = client.blobs.create_download_task(
file_items=[
CreateDownloadTaskFileItem(
file_id="file-1",
canvas_id="canvas-1",
node_id="node-1",
business_refs=[
DownloadTaskBusinessRef(ref_type="canvas", ref_id="canvas-1"),
],
),
CreateDownloadTaskFileItem(
file_id="file-2",
canvas_id="canvas-1",
node_id="node-2",
),
],
download_name="materials.zip",
business_refs=[
DownloadTaskBusinessRef(ref_type="download_scene", ref_id="export-panel"),
],
)
download_id = resp.task.download_id
print(resp.task.status, resp.task.progress)
file_items 也可以直接传 ["file-1", "file-2"]。如果业务需要审计下载来源,建议显式传 canvas_id、node_id 或 business_refs。
blob.get_download_task_status
查询下载任务状态。常见状态包括 created、pending、queued、processing、completed、failed、expired、cancelled。
get_download_task_status(
download_ids: List[str] = None,
*,
statuses: List[str] = None,
modes: List[str] = None,
download_name: str = None,
limit: int = None,
offset: int = None,
include_items: bool = None,
) → GetDownloadTaskStatusResponse
status_resp = client.blobs.get_download_task_status(
download_ids=[download_id],
download_name="canvas-selection",
include_items=True,
)
task = status_resp.tasks[0]
print(task.status, task.progress, task.success_count, task.failed_count)
for failed in task.failed_files:
print(failed.file_id, failed.error.error_code if failed.error else None)
failed_files 和 items[].error 返回结构化错误码、阶段、参数和诊断 ID;面向用户的多语言文案应由调用方或 Gateway 根据 error_code 自行翻译。
blob.generate_download_task_url
为已完成任务生成下载 URL。该 URL 可能指向单文件直链,也可能指向归档 zip;服务端会根据任务结果和区域策略选择可用存储。
generate_download_task_url(
download_id: str,
*,
expire_seconds: int = None,
network_preference: str = None,
) → GenerateDownloadTaskUrlResponse
if task.status == "completed" and task.result and task.result.can_download:
url_resp = client.blobs.generate_download_task_url(
download_id=download_id,
expire_seconds=900,
network_preference="default", # default | public | internal | fallback
)
print(url_resp.url, url_resp.download_name)
blob.run_download_task_dispatch_sweep
触发一次有界的下载任务投递补偿扫描。这个接口通常只给 Gateway cron、Cloud Scheduler 或内部运维任务调用,业务用户请求路径不应直接调用。
run_download_task_dispatch_sweep(
*,
max_dispatches: int = None,
max_concurrency: int = None,
lock_seconds: int = None,
) → DownloadTaskSweepResponse
blob.run_download_task_reconcile_sweep
触发一次有界的下载任务结果对账扫描,用于兜底 Executor 回调丢失或任务长时间停留在 queued / processing 的情况。
run_download_task_reconcile_sweep(
*,
max_reconcile: int = None,
max_concurrency: int = None,
lock_seconds: int = None,
stale_after_seconds: int = None,
) → DownloadTaskSweepResponse
blob.get_gcs_url
获取文件的 GCS 对象 URL。
get_gcs_url(file_id: str) → GetGcsUrlResponse
blob.batch_get_gcs_url
批量获取 GCS 对象 URL。
batch_get_gcs_url(file_ids: List[str], *, variant_name: str = "original") → BatchGcsUrlResponse
variant_name 默认 original;指定压缩变体不存在时,服务端会按回退链返回可用版本。
blob.get_compression_status
查询文件压缩状态。
get_compression_status(file_id: str) → CompressionStatusResponse
status = client.blobs.get_compression_status(file_id="image-456")
print(status.status) # "pending" | "processing" | "completed" | "failed"
blob.get_compressed_variants
获取文件的所有压缩变体信息。
get_compressed_variants(
file_id: str,
*,
variant_type: str = None, # 按类型过滤
) → GetVariantsResponse
variants = client.blobs.get_compressed_variants(file_id="image-456")
for v in variants.variants:
print(v.variant_name, v.width, v.height, v.file_size)
blob.trigger_recompression
触发重新压缩。
trigger_recompression(
file_id: str,
*,
force_reprocess: bool = False,
) → RecompressionResponse
blob.generate_variant_download_url
获取指定压缩变体的下载 URL。
generate_variant_download_url(
file_id: str,
variant_name: str, # e.g. "thumbnail", "medium", "720p"
*,
expire_seconds: int = 3600,
is_cdn: bool = False,
network_preference: str = None,
) → VariantDownloadUrlResponse
resp = client.blobs.generate_variant_download_url(file_id="img-1", variant_name="thumbnail")
print(resp.url)
blob.batch_get_file_status
批量查询文件的上传 / 压缩 / 同步三态。
batch_get_file_status(
file_ids: List[str],
*,
include_details: bool = False,
) → BatchFileStatusResponse
batch = client.blobs.batch_get_file_status(file_ids=["a", "b"], include_details=True)
for s in batch.statuses:
print(s.file_id, s.upload_status, s.compression_status, s.sync_status)
FileService — 元数据与分享
file.get_file
获取单个文件详情。
get_file(file_id: str) → GetFileResponse
file.get_files
批量获取文件详情。
get_files(file_ids: List[str]) → GetFilesResponse
file.list_files
分页列出文件,支持多维度过滤。
list_files(
folder_id: str = None,
file_name: str = None,
file_type: List[str] = None,
created_by_role: str = None, # "account" | "agent" | "system"
created_by: str = None,
page_size: int = 20,
page: int = 1,
) → FileListResponse
resp = client.files.list_files(folder_id="folder-1", file_type=["image", "video"], page_size=50)
for f in resp.files:
print(f.id, f.file_name)
file.rename_file
rename_file(file_id: str, new_name: str) → File
file.delete_file
delete_file(file_id: str) → None
file.generate_share_link
创建分享链接,支持密码、过期、次数限制。
generate_share_link(
file_id: str,
*,
is_public: bool = True,
access_scope: str = "view",
expire_seconds: int = 86400,
max_access: int = None,
share_password: str = None,
) → str
share_id = client.files.generate_share_link(
file_id="file-123",
expire_seconds=86400 * 7,
max_access=100,
share_password="s3cret",
)
file.visit_file
记录文件访问(审计日志)。
visit_file(
file_share_id: str,
access_type: str = "view",
access_duration: int = 0,
metadata: Dict[str, Any] = None,
) → None
file.get_compression_status
get_compression_status(file_id: str) → CompressionStatusResponse
file.get_compressed_variants
get_compressed_variants(file_id: str, *, variant_type: str = None) → GetVariantsResponse
file.trigger_recompression
trigger_recompression(file_id: str, *, force_reprocess: bool = False) → RecompressionResponse
file.generate_variant_download_url
generate_variant_download_url(
file_id: str,
variant_name: str,
*,
expire_seconds: int = 3600,
is_cdn: bool = False,
network_preference: str = None,
) → VariantDownloadUrlResponse
file.import_from_gcs
从 GCS URI 导入文件元数据。
import_from_gcs(
gcs_uri: str,
operation_type: str,
*,
folder_id: str = None,
file_name: str = None,
keep_original_filename: bool = False,
created_by_role: str = None,
created_by: str = None,
) → ImportFromGcsResponse
resp = client.files.import_from_gcs(
gcs_uri="gs://bucket/path/to/file.pdf",
operation_type="copy",
folder_id="folder-1",
)
file.generate_signed_gcs_url
生成 GCS 签名 URL。
generate_signed_gcs_url(gcs_uri: str, *, expire_seconds: int = None) → SignedGcsUrlResponse
FolderService — 文件夹管理
folder.create_folder
create_folder(folder_name: str, *, parent_id: str = None) → FolderInfo
folder = client.folders.create_folder(folder_name="Documents", parent_id="root")
folder.get_folder
get_folder(folder_id: str) → FolderInfo
folder.get_or_create_default_folder
get_or_create_default_folder(default_folder_name: str) → FolderInfo
folder.rename_folder
rename_folder(folder_id: str, new_name: str) → FolderInfo
folder.move_folder
move_folder(folder_id: str, new_parent_id: str) → FolderInfo
folder.update_folder
update_folder(
folder_id: str,
folder_name: str = None,
parent_id: str = None,
parent_id_provided: bool = False,
) → FolderInfo
folder.delete_folder
delete_folder(folder_id: str) → None
folder.list_folders
list_folders(
parent_id: str = None,
folder_name: str = None,
created_by_role: str = None,
created_by: str = None,
) → FolderListResponse
folders = client.folders.list_folders(parent_id="root")
for f in folders.items:
print(f.id, f.folder_name)
folder.list_folder_tree
list_folder_tree() → FolderTreeResponse
DocumentService — 文档管理
document.create_document
create_document(
title: str,
content: dict = None,
*,
folder_id: str = None,
default_folder_name: str = None,
) → DocumentWithContentResponse
doc = client.documents.create_document(
title="会议纪要",
content={"blocks": []},
folder_id="folder-1",
)
print(doc.document.id, doc.content)
document.get_document
get_document(
*,
document_id: str = None,
content_id: str = None,
record_visit: bool = False,
ip_address: str = None,
device: str = None,
referrer: str = None,
) → DocumentWithContentResponse
document.update_document
update_document(
document_id: str,
content: dict,
*,
title: str = None,
) → DocumentWithContentResponse
document.delete_document
delete_document(document_id: str) → None
document.search_documents
search_documents(
*,
search_text: str = None,
limit: int = 100,
offset: int = 0,
) → DocumentListResponse
document.list_owner_documents
list_owner_documents(
*,
user_id: str = None,
limit: int = 100,
offset: int = 0,
) → DocumentListResponse
document.list_recently_visited_documents
list_recently_visited_documents(
*,
user_id: str = None,
limit: int = 100,
offset: int = 0,
) → DocumentListResponse
document.share_document
share_document(
document_id: str,
target_user_id: str,
*,
permission_type: str = "viewer",
) → DocumentPermissionResponse
document.clone_document
clone_document(
source_document_id: str,
target_org_id: str,
target_user_id: str,
*,
target_folder_id: str = None,
new_document_title: str = None,
default_folder_name: str = None,
) → CloneDocumentResponse
document.append_document_content
append_document_content(
document_id: str,
content: dict,
*,
target_org_id: str = None,
target_user_id: str = None,
) → AppendDocumentContentResponse
document.update_document_content
update_document_content(
document_id: str,
content_id: str,
content: dict,
*,
target_org_id: str = None,
target_user_id: str = None,
) → DocumentWithContentResponse
document.add_document_agent_operations
add_document_agent_operations(operations: List[Dict[str, Any]]) → AddDocumentAgentOperationsResponse
TapleService — 协同电子表格
Taple 操作支持
idempotency_key参数实现幂等;行/列/单元格操作支持sheet_version(乐观锁)和client_id(客户端标识)。
taple.create_table
create_table(
name: str,
*,
folder_id: str = None,
description: str = None,
idempotency_key: str = None,
) → TableResponse
table = client.taples.create_table(name="Sales Q1", folder_id="folder-1")
taple.get_table
通过 table_id 或 file_id 获取表详情。
get_table(*, table_id: str = None, file_id: str = None) → TableResponse
taple.update_table
update_table(table_id: str, *, name: str = None, description: str = None) → TableResponse
taple.delete_table
delete_table(table_id: str) → None
taple.create_sheet
create_sheet(
table_id: str,
name: str,
*,
description: str = None,
position: int = None,
) → SheetResponse
taple.get_sheet
get_sheet(sheet_id: str) → SheetResponse
taple.list_sheets
list_sheets(table_id: str) → ListSheetsResponse
taple.update_sheet
update_sheet(
sheet_id: str,
*,
name: str = None,
description: str = None,
position: int = None,
) → SheetResponse
taple.delete_sheet
delete_sheet(sheet_id: str) → None
taple.get_sheet_version
获取 Sheet 当前版本号(用于乐观锁)。
get_sheet_version(sheet_id: str) → Any
taple.get_sheet_data
获取 Sheet 完整数据,可指定版本。
get_sheet_data(sheet_id: str, version: int = None) → Any
taple.batch_edit_sheet
批量混合编辑,一次请求中包含行/列/单元格操作。
batch_edit_sheet(
sheet_id: str,
operations: List[Any],
sheet_version: int = None,
client_id: str = None,
) → Any
taple.create_column
create_column(
sheet_id: str,
name: str,
column_type: str = "text",
*,
sheet_version: int = None,
client_id: str = None,
position: int = None,
width: int = None,
description: str = None,
properties: Dict[str, Any] = None,
) → ColumnResponse
col = client.taples.create_column(sheet_id="sheet-1", name="Revenue", column_type="number")
taple.update_column
update_column(
sheet_id: str,
column_key: str,
*,
sheet_version: int = None,
client_id: str = None,
name: str = None,
column_type: str = None,
width: int = None,
hidden: bool = None,
description: str = None,
properties: Dict[str, Any] = None,
) → ColumnResponse
taple.delete_column
delete_column(
sheet_id: str,
column_key: str,
*,
sheet_version: int = None,
client_id: str = None,
) → None
taple.batch_edit_columns
batch_edit_columns(
sheet_id: str,
operations: List[Dict[str, Any]],
*,
sheet_version: int = None,
client_id: str = None,
) → Any
taple.get_column_data
get_column_data(sheet_id: str, column_key: str) → Any
taple.create_row
create_row(
sheet_id: str,
*,
sheet_version: int = None,
client_id: str = None,
position: int = None,
height: int = None,
hidden: bool = None,
) → RowResponse
taple.update_row
update_row(
sheet_id: str,
row_key: str,
*,
sheet_version: int = None,
client_id: str = None,
position: int = None,
height: int = None,
hidden: bool = None,
) → RowResponse
taple.delete_row
delete_row(
sheet_id: str,
row_key: str,
*,
sheet_version: int = None,
client_id: str = None,
) → None
taple.batch_edit_rows
batch_edit_rows(
sheet_id: str,
operations: List[Dict[str, Any]],
*,
sheet_version: int = None,
client_id: str = None,
) → Any
taple.get_row_data
get_row_data(sheet_id: str, row_key: str) → Any
taple.edit_cell
编辑单元格内容。
edit_cell(
sheet_id: str,
column_key: str,
row_key: str,
*,
sheet_version: int = None,
client_id: str = None,
raw_value: str = None,
formatted_value: str = None,
formula: str = None,
data_type: str = None,
styles: Dict[str, Any] = None,
) → CellResponse
client.taples.edit_cell(
sheet_id="sheet-1",
column_key="col-revenue",
row_key="row-1",
raw_value="15000",
data_type="number",
)
taple.delete_cell
delete_cell(
sheet_id: str,
column_key: str,
row_key: str,
*,
sheet_version: int = None,
client_id: str = None,
) → None
taple.batch_edit_cells
batch_edit_cells(
sheet_id: str,
operations: List[Dict[str, Any]],
*,
sheet_version: int = None,
client_id: str = None,
) → Any
client.taples.batch_edit_cells(
sheet_id="sheet-1",
operations=[
{"column_key": "col-name", "row_key": "row-1", "raw_value": "Alice"},
{"column_key": "col-name", "row_key": "row-2", "raw_value": "Bob"},
],
)
taple.get_cell_data
get_cell_data(sheet_id: str, column_key: str, row_key: str) → Any
taple.create_table_view
create_table_view(
sheet_id: str,
name: str,
view_type: str,
*,
filter_criteria: Dict[str, Any] = None,
sort_criteria: Dict[str, Any] = None,
visible_columns: Dict[str, bool] = None,
group_criteria: Dict[str, Any] = None,
is_hidden: bool = False,
is_default: bool = False,
config: Dict[str, Any] = None,
) → TableViewResponse
view = client.taples.create_table_view(
sheet_id="sheet-1",
name="High Revenue",
view_type="filter",
filter_criteria={"column": "Revenue", "operator": ">", "value": "10000"},
sort_criteria={"column": "Revenue", "order": "desc"},
)
taple.get_table_view
get_table_view(view_id: str) → TableViewResponse
taple.list_table_views
list_table_views(
*,
table_id: str = None,
sheet_id: str = None,
view_type: str = None,
) → ListTableViewsResponse
taple.update_table_view
update_table_view(
view_id: str,
*,
name: str = None,
filter_criteria: Dict[str, Any] = None,
sort_criteria: Dict[str, Any] = None,
visible_columns: Dict[str, bool] = None,
group_criteria: Dict[str, Any] = None,
is_hidden: bool = None,
is_default: bool = None,
config: Dict[str, Any] = None,
) → TableViewResponse
taple.update_table_view_config
快捷更新视图配置(仅 config 字段)。
update_table_view_config(view_id: str, config: Dict[str, Any]) → TableViewResponse
taple.delete_table_view
delete_table_view(view_id: str) → None
taple.batch_create_table_views
batch_create_table_views(sheet_id: str, views: List[Dict[str, Any]]) → BatchCreateTableViewsResponse
taple.clone_table_data
克隆表到另一用户/组织。
clone_table_data(
source_table_id: str,
target_org_id: str,
target_user_id: str,
*,
target_folder_id: str = None,
new_table_name: str = None,
include_views: bool = False,
) → CloneTableDataResponse
clone = client.taples.clone_table_data(
source_table_id="table-1",
target_org_id="org-other",
target_user_id="user-other",
new_table_name="Sales Copy",
include_views=True,
)
taple.export_table_data
导出表数据,支持多种格式。
export_table_data(
table_id: str,
format: ExportFormat, # CSV | XLSX | JSON | PDF | DOCX | PPTX | HTML | MARKDOWN
*,
sheet_ids: List[str] = None,
options: Dict[str, Any] = None,
) → ExportTableDataResponse
from file_hub_client import ExportFormat
export = client.taples.export_table_data(table_id="table-1", format=ExportFormat.CSV)
# export.exported_file_id 可通过 blobs.download 下载
taple.import_table_data
从文件导入数据到表。
import_table_data(
file_id: str,
*,
target_table_id: str = None, # 导入到已有表
table_name: str = None, # 或创建新表
folder_id: str = None,
import_mode: str = "APPEND", # APPEND | OVERWRITE
skip_first_row: bool = True,
auto_detect_types: bool = True,
clear_existing_data: bool = False,
column_mapping: Dict[str, str] = None,
date_format: str = "YYYY-MM-DD",
csv_delimiter: str = ",",
csv_encoding: str = "UTF-8",
max_rows: int = 0, # 0 = 不限
) → ImportTableDataResponse
# 先上传 CSV
upload = client.blobs.upload(file=Path("/data/sales.csv"))
# 导入到新表
result = client.taples.import_table_data(
file_id=upload.file.id,
table_name="Imported Sales",
folder_id="folder-1",
import_mode="APPEND",
auto_detect_types=True,
)
数据模型
所有响应模型基于 Pydantic v2,支持 .model_dump() 序列化。主要类型:
- 文件:
File、UploadFile、FileUploadResponse、UploadUrlResponse、FileListResponse、GetFileResponse - 压缩:
CompressedVariant、CompressionStatusResponse、GetVariantsResponse - 状态枚举:
FileUploadStatus、FileCompressionStatus、FileSyncStatus - 状态聚合:
FileStatusInfo、BatchFileStatusResponse - 文件夹:
FolderInfo、FolderListResponse、FolderTreeNode、FolderTreeResponse - 文档:
DocumentInfo、DocumentWithContentResponse、DocumentListResponse、DocumentPermissionResponse - 上下文:
UserContext、RequestContext、Role - Taple:
TableResponse、SheetResponse、ColumnResponse、RowResponse、CellResponse、TableViewResponse - 导入导出:
ExportFormat、ExportTableDataResponse、ImportTableDataResponse、CloneTableDataResponse
异常
全部继承自 FileHubError:
| 异常 | 触发 |
|---|---|
FileNotFoundError |
文件不存在 |
FolderNotFoundError |
文件夹不存在 |
DocumentNotFoundError |
文档不存在 |
UploadError |
上传失败 |
DownloadError |
下载失败 |
ExportError |
导出 / 转换失败 |
StorageError |
存储后端错误 |
PermissionError |
鉴权 / 权限不足 |
ValidationError |
参数校验失败 |
ConnectionError |
gRPC 连接失败 |
TimeoutError |
操作超时 |
每个异常包含 .code / .message / .details。OSS multipart 分片上传失败时,UploadError 还会提供 .file_id、.upload_id、.part_number,用于保存断点并继续恢复上传。
from file_hub_client.exceptions import FileNotFoundError, UploadError
try:
client.blobs.download_to_bytes(file_id="nonexistent")
except FileNotFoundError as e:
print(e.message, e.details)
传输细节
- 主通道:gRPC over HTTP/2,支持 TLS;SDK 内部以单 channel 起步,并按活跃 RPC 数在请求路径上惰性扩展多 channel。
- 大文件:上传 / 下载默认通过服务端签发的预签名 URL 走 HTTP 直传/直下,绕过 gRPC 消息体大小限制;SDK 内置
HttpUploader/HttpDownloader处理分块、续传、流式。 - MIME 探测:上传时若未显式提供
mime_type,通过python-magic读取魔数自动判定(覆盖 26+ 常见格式)。 - 重试:仅对明确查询类 gRPC 方法(如
Get/List/Search/BatchGet)在明显传输故障时做一次换 channel failover retry;上传、创建、更新、删除和Generate*Url默认不自动重试。
开发
# 编辑安装
pip install -e ".[dev]"
# 生成 gRPC 代码
python make_grpc.py
# 测试
pytest tests/
TEST_MODE=async pytest tests/file/ # 仅异步用例
TEST_MODE=sync pytest tests/file/ # 仅同步用例
# 构建发行包
python setup.py sdist bdist_wheel
设计要点
- 同步 / 异步双 API,签名一致 — 上层应用按运行环境选用,不需要重写业务代码。
- gRPC + HTTP 混合传输 — 元数据走 gRPC,大文件走预签名 URL,兼顾低延迟与大对象支持。
- 上下文驱动 —
UserContext/RequestContext在每个调用透传,服务端据此做归属、权限、审计。 - 状态聚合接口 —
batch_get_file_status一次拿到上传 / 压缩 / 同步三态,便于客户端轮询。 - 类型完备 — Pydantic v2 +
py.typed,IDE 提示与静态检查友好。
Metadata
Release files for tamar-file-hub-client 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| tamar_file_hub_client-0.4.2.tar.gz | 198.5 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tamar_file_hub_client-0.4.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 400.3 kB
Release files / tamar_file_hub_client-0.4.2.tar.gz
| Download URL | tamar_file_hub_client-0.4.2.tar.gz |
|---|---|
| Size | 198.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2bf2016ac7f3f0afc05221d36378e3eebdcab5f18963fd6d5753d761b05fc76
|
|
BLAKE2b-256 checksum How to use checksums |
c410a3166b3b9527ee4c4fac2fcf616e64c2df718519fe1668df5d61b78d983f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Release files / tamar_file_hub_client-0.4.2-py3-none-any.whl
| Download URL | tamar_file_hub_client-0.4.2-py3-none-any.whl |
|---|---|
| Size | 201.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
df46033cbf561b4de1a077769753058b43f03593efc757bde2eaaaa6a18235c6
|
|
BLAKE2b-256 checksum How to use checksums |
fbb903340de3b5eb96503febbe08993e5be4995b199eea207b6ca1b4142bf7da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Security for tamar-file-hub-client 0.4.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.