A Python SDK for gRPC-based file management system
Project description
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 导航
共 91 个公开方法,分布在 5 个服务中。所有方法的 request_id 和 **metadata 参数均为可选,用于链路追踪和 gRPC metadata 注入,下文参数表中省略。
BlobService (client.blobs) — 21 个方法
| 方法 | 说明 |
|---|---|
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 |
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.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 提示与静态检查友好。
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tamar_file_hub_client-0.4.1.tar.gz.
File metadata
- Download URL: tamar_file_hub_client-0.4.1.tar.gz
- Upload date:
- Size: 194.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30f1e6ed95cade9d3ad6c50c1583f5402624e3c9535e77f141c33c6335fb88e0
|
|
| MD5 |
721596ce5fad0e9c3d799832834c13a2
|
|
| BLAKE2b-256 |
fa93af69f79332df4ef70d82588143af9336bce06b2d210e288fbef08c7361a1
|
File details
Details for the file tamar_file_hub_client-0.4.1-py3-none-any.whl.
File metadata
- Download URL: tamar_file_hub_client-0.4.1-py3-none-any.whl
- Upload date:
- Size: 200.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
497b5916b011b18ca4be6c58bc00a1775b28ac60ce6f9c4105bc3e9e40c0a569
|
|
| MD5 |
938e3fcd4d17ca6bb93bde22dcc4e3f1
|
|
| BLAKE2b-256 |
7dce45c6e1e5720a192e2cc52b06b13cb8d4365e9f39a5df12b088de072d2fe8
|