tamar-file-hub-client 0.1.7__py3-none-any.whl → 0.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2453 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: tamar-file-hub-client
3
- Version: 0.1.7
4
- Summary: A Python SDK for gRPC-based file management system
5
- Home-page: https://github.com/Tamar-Edge-AI/file-hub-client
6
- Author: Oscar Ou
7
- Author-email: oscar.ou@tamaredge.ai
8
- License: MIT
9
- Classifier: Development Status :: 4 - Beta
10
- Classifier: Intended Audience :: Developers
11
- Classifier: License :: OSI Approved :: MIT License
12
- Classifier: Operating System :: OS Independent
13
- Classifier: Programming Language :: Python :: 3
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
- Classifier: Topic :: Internet :: WWW/HTTP
21
- Classifier: Topic :: Communications :: File Sharing
22
- Classifier: Framework :: AsyncIO
23
- Classifier: Typing :: Typed
24
- Requires-Python: >=3.8
25
- Description-Content-Type: text/markdown
26
- Requires-Dist: grpcio>=1.67.1
27
- Requires-Dist: grpcio-tools>=1.67.1
28
- Requires-Dist: protobuf>=4.21.0
29
- Requires-Dist: pydantic>=2.0.0
30
- Requires-Dist: typing-extensions>=4.0.0; python_version < "3.10"
31
- Requires-Dist: requests>=2.28.0
32
- Requires-Dist: aiohttp>=3.8.0
33
- Requires-Dist: aiofiles>=23.0.0
34
- Requires-Dist: python-magic; platform_system != "Windows"
35
- Requires-Dist: python-magic-bin~=0.4.14; platform_system == "Windows"
36
- Dynamic: author
37
- Dynamic: author-email
38
- Dynamic: classifier
39
- Dynamic: description
40
- Dynamic: description-content-type
41
- Dynamic: home-page
42
- Dynamic: license
43
- Dynamic: requires-dist
44
- Dynamic: requires-python
45
- Dynamic: summary
46
-
47
- # File Hub Client
48
-
49
- 一个基于 gRPC 的文件管理系统 Python SDK,提供异步和同步两种客户端实现。
50
-
51
- ## 功能特性
52
-
53
- - 🚀 **双模式支持**:提供异步(AsyncIO)和同步两种客户端实现
54
- - 📁 **完整的文件管理**:支持文件上传、下载、重命名、删除等操作
55
- - 📂 **文件夹管理**:支持文件夹的创建、重命名、移动、删除
56
- - 🔗 **文件分享**:支持生成分享链接,设置访问权限和密码
57
- - 🔄 **多种上传方式**:支持直传、断点续传、客户端直传到对象存储
58
- - 🎯 **智能MIME类型检测**:支持26+种主流文件格式的魔术字节检测和扩展名推断
59
- - 🤖 **AI生成文件支持**:完美支持AI模型输出的字节数据+MIME类型组合上传
60
- - 🛡️ **错误处理**:完善的异常体系和错误重试机制
61
- - 🔒 **TLS/SSL 支持**:支持安全的加密连接,保护数据传输
62
- - 🔁 **自动重试**:连接失败时自动重试,提高可靠性
63
- - 📝 **类型注解**:完整的类型提示支持
64
- - 🧩 **模块化设计**:清晰的代码结构,易于扩展
65
- - 🎨 **图片和视频压缩**:支持多种规格的媒体文件压缩变体生成和管理
66
- - 🏗️ **分层服务架构**:文件服务分为传统文件(blob)和自定义类型(结构化数据),每种类型独立服务,语义清晰
67
- - 🔧 **环境变量配置**:支持通过环境变量配置所有参数
68
- - 👤 **用户上下文管理**:支持区分资源所有权(ownership)和操作者(operator)
69
- - 📊 **请求上下文追踪**:自动收集客户端信息,支持请求追踪和审计
70
- - 📊 **Taple 电子表格**:完整的类 Excel 功能支持,包括数据导入导出、查询筛选、样式管理等
71
- - 📡 **gRPC 请求日志**:自动记录所有 gRPC 请求和响应,支持 JSON 格式日志
72
-
73
- ## 项目结构
74
-
75
- ```
76
- file-hub-client/
77
- ├── file_hub_client/ # 主包目录
78
- │ ├── __init__.py # 包初始化,导出版本信息和主要类
79
- │ ├── client.py # 客户端入口(AsyncTamarFileHubClient, TamarFileHubClient)
80
- │ ├── py.typed # PEP 561 类型标记文件
81
- │ │
82
- │ ├── rpc/ # gRPC 相关
83
- │ │ ├── __init__.py # RPC 模块初始化
84
- │ │ ├── async_client.py # 异步 gRPC 客户端基类
85
- │ │ ├── sync_client.py # 同步 gRPC 客户端基类
86
- │ │ ├── interceptors.py # gRPC 拦截器(自动日志记录)
87
- │ │ ├── generate_grpc.py # Proto 文件代码生成脚本
88
- │ │ ├── protos/ # Protocol Buffer 定义
89
- │ │ │ ├── file_service.proto # 文件服务定义
90
- │ │ │ ├── folder_service.proto # 文件夹服务定义
91
- │ │ │ └── taple_service.proto # Taple 服务定义
92
- │ │ └── gen/ # 生成的 gRPC 代码(自动生成)
93
- │ │ ├── __init__.py
94
- │ │ ├── file_service_pb2.py
95
- │ │ ├── file_service_pb2_grpc.py
96
- │ │ ├── folder_service_pb2.py
97
- │ │ └── folder_service_pb2_grpc.py
98
- │ │
99
- │ ├── services/ # 服务层(分层架构:传统文件用blob_service,自定义类型独立service)
100
- │ │ ├── __init__.py # 服务模块导出
101
- │ │ ├── file/ # 文件服务(统一入口,按类型分层)
102
- │ │ │ ├── __init__.py
103
- │ │ │ ├── base_file_service.py # 文件服务基类
104
- │ │ │ ├── async_blob_service.py # 异步二进制大对象服务(传统文件上传下载)
105
- │ │ │ ├── sync_blob_service.py # 同步二进制大对象服务(传统文件上传下载)
106
- │ │ │ ├── async_file_service.py # 异步文件元数据服务(所有类型通用)
107
- │ │ │ └── sync_file_service.py # 同步文件元数据服务(所有类型通用)
108
- │ │ │ # 未来扩展:spreadsheet_service, document_service, canvas_service等
109
- │ │ ├── folder/ # 文件夹服务
110
- │ │ │ ├── __init__.py
111
- │ │ │ ├── async_folder_service.py # 异步文件夹服务实现
112
- │ │ │ └── sync_folder_service.py # 同步文件夹服务实现
113
- │ │ └── taple/ # Taple(电子表格)服务
114
- │ │ ├── __init__.py
115
- │ │ ├── base_taple_service.py # Taple 服务基类
116
- │ │ ├── async_taple_service.py # 异步 Taple 服务实现
117
- │ │ ├── sync_taple_service.py # 同步 Taple 服务实现
118
- │ │ └── idempotent_taple_mixin.py # 幂等性支持混入类
119
- │ │
120
- │ ├── schemas/ # 数据模型(Pydantic)
121
- │ │ ├── __init__.py # 模型导出
122
- │ │ ├── file.py # 文件相关模型
123
- │ │ ├── folder.py # 文件夹相关模型
124
- │ │ ├── context.py # 上下文相关模型(用户和请求上下文)
125
- │ │ └── taple.py # Taple 相关模型
126
- │ │
127
- │ ├── enums/ # 枚举定义
128
- │ │ ├── __init__.py # 枚举导出
129
- │ │ ├── role.py # 角色枚举(ACCOUNT, AGENT, SYSTEM)
130
- │ │ ├── upload_mode.py # 上传模式枚举
131
- │ │ └── export_format.py # 导出格式枚举
132
- │ │
133
- │ ├── errors/ # 异常定义
134
- │ │ ├── __init__.py # 异常导出
135
- │ │ └── exceptions.py # 自定义异常类
136
- │ │
137
- │ └── utils/ # 工具函数
138
- │ ├── __init__.py # 工具函数导出
139
- │ ├── file_utils.py # 文件操作工具
140
- │ ├── converter.py # 数据转换工具
141
- │ ├── retry.py # 重试装饰器
142
- │ ├── upload_helper.py # 上传辅助工具(HTTP上传器)
143
- │ ├── download_helper.py # 下载辅助工具(HTTP下载器)
144
- │ ├── idempotency.py # 幂等性支持工具
145
- │ └── logging.py # 日志配置和工具
146
-
147
- ├── tests/ # 测试文件
148
- │ └── taple/ # Taple 功能测试
149
- │ ├── config.py # 测试配置
150
- │ ├── test_*.py # 各种功能测试脚本
151
- │ └── run_all_tests.py # 运行所有测试
152
-
153
- ├── .gitignore # Git 忽略文件配置
154
- ├── .env.example # 环境变量配置示例
155
- ├── README.md # 项目说明文档(本文件)
156
- ├── setup.py # 安装配置文件
157
- ├── pyproject.toml # 项目配置文件(PEP 518)
158
- └── MANIFEST.in # 打包配置文件
159
- ```
160
-
161
- ## 模块说明
162
-
163
- ### 核心模块
164
-
165
- - **client.py**: 提供 `AsyncTamarFileHubClient` 和 `TamarFileHubClient` 两个客户端类,是使用 SDK 的入口点
166
- - 提供了预配置的单例客户端 `tamar_client` 和 `async_tamar_client`
167
- - 支持分层服务访问:
168
- - `blobs`(传统文件内容:上传/下载)
169
- - `files`(文件元数据:所有类型通用的管理操作)
170
- - `folders`(文件夹管理)
171
- - `taples`(电子表格服务)
172
- - 未来扩展:`documents`、`canvases` 等自定义类型服务
173
-
174
- ### RPC 模块 (`rpc/`)
175
-
176
- - **async_client.py/sync_client.py**: gRPC 客户端基类,处理连接管理、元数据构建、stub 缓存
177
- - **interceptors.py**: gRPC 拦截器,自动记录所有请求和响应日志
178
- - **generate_grpc.py**: 从 proto 文件生成 Python 代码的脚本
179
- - **protos/**: 存放 Protocol Buffer 定义文件
180
- - `file_service.proto`: 定义文件相关的 RPC 服务
181
- - `folder_service.proto`: 定义文件夹相关的 RPC 服务
182
- - `taple_service.proto`: 定义 Taple 电子表格相关的 RPC 服务
183
-
184
- ### 服务模块 (`services/`)
185
-
186
- #### 分层服务架构设计
187
-
188
- File Hub Client 采用分层服务架构,将文件服务按类型和语义进行清晰分离:
189
-
190
- **📁 统一文件入口**:所有文件类型都通过统一的 `files` 接口进行元数据管理(获取、重命名、删除、列表等)
191
-
192
- **🔄 按类型分层服务**:
193
- - **传统文件类型**(PDF、图片、视频等)→ `blob_service` 处理
194
- - 核心操作:**上传** 和 **下载**
195
- - 特点:二进制数据,重点是存储和传输
196
-
197
- - **自定义文件类型**(在线表格、文档、画布等)→ 每种类型独立 `service`
198
- - 核心操作:**创建** 和 **导出**
199
- - 特点:结构化数据,重点是数据操作和格式转换
200
-
201
- **🎯 设计优势**:
202
- - **语义清晰**:不同类型的文件使用不同的操作语义,更符合实际使用场景
203
- - **易于扩展**:新增自定义文件类型时,只需添加对应的独立服务
204
- - **职责分离**:每个服务专注于特定类型的操作,代码更易维护
205
- - **SDK 友好**:为 SDK 使用者提供更直观的 API 设计,而非通用的 REST API
206
-
207
- #### 具体实现
208
-
209
- - **file/**: 文件服务实现
210
- - **blob_service**: 处理传统文件(二进制大对象)
211
- - 支持多种上传模式(普通上传、流式上传、断点续传)
212
- - 智能选择上传模式(根据文件大小)
213
- - 生成上传/下载 URL
214
- - 支持临时文件上传
215
- - **媒体文件压缩**:支持图片和视频的多规格压缩变体生成
216
- - **压缩管理**:获取压缩状态、管理变体、触发重新压缩
217
- - 适用类型:PDF、图片、视频、音频、压缩包等
218
- - **file_service**: 处理文件元数据操作(所有类型通用)
219
- - 获取、重命名、删除文件
220
- - 列出文件
221
- - 生成分享链接
222
- - 记录文件访问
223
- - **[future] document_service**: 在线文档服务(规划中)
224
- - 创建文档、编辑内容、插入元素
225
- - 导出为 Word、PDF、HTML 等格式
226
- - **[future] canvas_service**: 画布服务(规划中)
227
- - 创建画布、绘制图形、添加元素
228
- - 导出为 PNG、SVG、PDF 等格式
229
-
230
- - **folder/**: 文件夹服务实现
231
- - 创建、重命名、移动、删除文件夹
232
- - 列出文件夹内容
233
-
234
- - **taple/**: Taple 电子表格服务实现(已上线)
235
- - **taple_service**: 基础表格服务
236
- - 创建表格、工作表、列、行、单元格
237
- - 支持批量操作和乐观锁版本控制
238
- - 合并单元格和视图管理
239
- - **idempotent_taple_mixin**: 幂等性支持
240
- - 自动管理幂等性键
241
- - 防止重复操作
242
-
243
- ### 数据模型 (`schemas/`)
244
-
245
- - **file.py**: 文件相关的数据模型
246
- - `File`: 文件信息
247
- - `FileUploadResponse`: 文件上传响应
248
- - `UploadUrlResponse`: URL上传响应
249
- - `ShareLinkRequest`: 分享链接请求
250
- - `FileListResponse`: 文件列表响应
251
- - `CompressedVariant`: 压缩变体信息
252
- - `CompressionStatusResponse`: 压缩状态响应
253
- - `GetVariantsResponse`: 获取变体响应
254
- - `RecompressionResponse`: 重新压缩响应
255
- - `VariantDownloadUrlResponse`: 变体下载URL响应
256
-
257
- - **folder.py**: 文件夹相关的数据模型
258
- - `FolderInfo`: 文件夹信息
259
- - `FolderListResponse`: 文件夹列表响应
260
-
261
- - **context.py**: 上下文相关的数据模型
262
- - `UserContext`: 用户上下文(组织、用户、角色、操作者)
263
- - `RequestContext`: 请求上下文(请求ID、客户端信息、追踪信息)
264
- - `FullContext`: 完整上下文
265
-
266
- - **taple.py**: Taple 相关的数据模型
267
- - `Table`: 表格信息
268
- - `Sheet`: 工作表信息
269
- - `Column`: 列信息
270
- - `Row`: 行信息
271
- - `Cell`: 单元格信息
272
- - `ConflictInfo`: 冲突信息
273
- - `BatchEditSheetResponse`: 批量编辑响应
274
-
275
- ### 枚举定义 (`enums/`)
276
-
277
- - **role.py**: 用户角色枚举(ACCOUNT、AGENT、SYSTEM)
278
- - **upload_mode.py**: 上传模式枚举(NORMAL、STREAM、RESUMABLE)
279
- - **export_format.py**: 导出格式枚举(XLSX、CSV、JSON、HTML、MARKDOWN)
280
-
281
- ### 工具模块 (`utils/`)
282
-
283
- - **file_utils.py**: 文件操作相关工具函数
284
- - `get_file_mime_type`: 获取文件 MIME 类型(支持自定义映射)
285
- - `split_file_chunks`: 文件分块
286
- - `calculate_file_hash`: 计算文件哈希
287
-
288
- - **converter.py**: 数据转换工具
289
- - `timestamp_to_datetime`: 时间戳转换
290
- - `convert_proto_to_model`: Proto 消息转模型
291
-
292
- - **retry.py**: 提供重试装饰器 `retry_with_backoff`
293
-
294
- - **upload_helper.py**: HTTP 上传辅助工具
295
- - `AsyncHttpUploader`: 异步 HTTP 上传器
296
- - `SyncHttpUploader`: 同步 HTTP 上传器
297
- - 支持普通上传和断点续传
298
-
299
- - **download_helper.py**: HTTP 下载辅助工具
300
- - `AsyncHttpDownloader`: 异步 HTTP 下载器
301
- - `SyncHttpDownloader`: 同步 HTTP 下载器
302
- - 支持流式下载和断点续传
303
-
304
- - **idempotency.py**: 幂等性支持工具
305
- - `IdempotencyKeyGenerator`: 幂等性键生成器
306
- - `IdempotencyManager`: 幂等性管理器
307
- - `generate_idempotency_key`: 生成幂等性键函数
308
-
309
- - **logging.py**: 日志配置和工具
310
- - `GrpcJSONFormatter`: JSON 格式化器
311
- - `GrpcRequestLogger`: gRPC 请求日志记录器
312
- - 支持中文日志消息和图标
313
-
314
- ### 错误处理 (`errors/`)
315
-
316
- - **exceptions.py**: 定义了完整的异常体系
317
- - `FileHubError`: 基础异常类
318
- - `FileNotFoundError`: 文件不存在
319
- - `FolderNotFoundError`: 文件夹不存在
320
- - `UploadError`: 上传错误
321
- - `DownloadError`: 下载错误
322
- - `ValidationError`: 验证错误
323
- - `ConnectionError`: 连接错误
324
- - `TimeoutError`: 超时错误
325
- - `PermissionError`: 权限错误
326
- - 等等...
327
-
328
- ## 安装
329
-
330
- ```bash
331
- pip install tamar-file-hub-client
332
- ```
333
-
334
- ## 配置
335
-
336
- ### 环境变量配置
337
-
338
- File Hub Client 支持通过环境变量配置连接参数,这在生产环境中特别有用。
339
-
340
- 1. **创建 `.env` 文件**:
341
- ```bash
342
- # 在项目根目录创建 .env 文件
343
- touch .env
344
- ```
345
-
346
- 2. **编辑 `.env` 文件**:
347
-
348
- **线上环境示例(使用域名,不需要端口)**:
349
- ```env
350
- # gRPC 服务器配置 - 线上环境
351
- FILE_HUB_HOST=api.filehub.example.com
352
- # FILE_HUB_PORT 不设置,使用域名默认端口
353
- FILE_HUB_SECURE=true
354
- FILE_HUB_API_KEY=your-api-key
355
-
356
- # 连接重试配置
357
- FILE_HUB_RETRY_COUNT=5
358
- FILE_HUB_RETRY_DELAY=2.0
359
- ```
360
-
361
- **本地开发环境示例(使用自定义端口)**:
362
- ```env
363
- # gRPC 服务器配置 - 本地开发
364
- FILE_HUB_HOST=localhost
365
- FILE_HUB_PORT=50051
366
- FILE_HUB_SECURE=false
367
- # FILE_HUB_API_KEY 本地开发可能不需要
368
-
369
- # 连接重试配置
370
- FILE_HUB_RETRY_COUNT=3
371
- FILE_HUB_RETRY_DELAY=1.0
372
- ```
373
-
374
- 3. **支持的环境变量**:
375
-
376
- | 环境变量 | 说明 | 默认值 |
377
- |---------|------|--------|
378
- | `FILE_HUB_HOST` | gRPC 服务器地址(域名或IP) | `localhost` |
379
- | `FILE_HUB_PORT` | gRPC 服务器端口(可选,不设置时直接使用HOST) | 无 |
380
- | `FILE_HUB_SECURE` | 是否启用 TLS/SSL | `false` |
381
- | `FILE_HUB_API_KEY` | API 认证密钥(可选) | 无 |
382
- | `FILE_HUB_RETRY_COUNT` | 连接重试次数 | `3` |
383
- | `FILE_HUB_RETRY_DELAY` | 重试延迟(秒) | `1.0` |
384
-
385
- ### TLS/SSL 配置
386
-
387
- 当 `FILE_HUB_SECURE` 设置为 `true` 时,客户端会使用 TLS 加密连接:
388
-
389
- - 默认使用系统的根证书
390
- - 如果提供了 `FILE_HUB_API_KEY`,会自动添加到请求头中进行认证
391
-
392
- ```python
393
- # 通过代码配置 TLS
394
- from file_hub_client import TamarFileHubClient
395
-
396
- # 方式1:使用域名(不需要指定端口)
397
- client = TamarFileHubClient(
398
- host="secure-server.com", # 只需要域名
399
- secure=True,
400
- credentials={"api_key": "your-api-key"}
401
- )
402
-
403
- # 方式2:使用自定义端口
404
- client = TamarFileHubClient(
405
- host="secure-server.com",
406
- port=8443, # 自定义端口
407
- secure=True,
408
- credentials={"api_key": "your-api-key"}
409
- )
410
- ```
411
-
412
- ### 端口配置说明
413
-
414
- 从 v0.0.3 版本开始,端口参数变为可选:
415
-
416
- - **线上环境**:通常只需要提供域名,不需要指定端口
417
- - **本地开发**:可以指定自定义端口
418
-
419
- ```python
420
- # 线上环境(使用标准端口)
421
- client = TamarFileHubClient(
422
- host="api.example.com", # 只提供域名
423
- secure=True
424
- )
425
-
426
- # 本地开发(使用自定义端口)
427
- client = TamarFileHubClient(
428
- host="localhost",
429
- port=50051, # 自定义端口
430
- secure=False
431
- )
432
- ```
433
-
434
- ### 连接重试
435
-
436
- 客户端支持自动重试连接,对于不稳定的网络环境特别有用:
437
-
438
- ```python
439
- # 通过代码配置重试
440
- from file_hub_client import TamarFileHubClient
441
-
442
- client = TamarFileHubClient(
443
- host="server.com",
444
- retry_count=5, # 重试5次
445
- retry_delay=2.0 # 每次重试间隔2秒
446
- )
447
- ```
448
-
449
- ### 日志配置
450
-
451
- File Hub Client 支持详细的 gRPC 请求日志记录:
452
-
453
- ```python
454
- from file_hub_client import AsyncTamarFileHubClient
455
-
456
- # 启用日志记录(默认启用)
457
- client = AsyncTamarFileHubClient(
458
- enable_logging=True,
459
- log_level="INFO" # DEBUG, INFO, WARNING, ERROR
460
- )
461
-
462
- # 日志输出示例(JSON格式):
463
- # {
464
- # "timestamp": "2025-07-15T17:30:00.123456",
465
- # "level": "INFO",
466
- # "type": "request",
467
- # "uri": "CreateFolder",
468
- # "request_id": "test-123",
469
- # "data": {
470
- # "folder_name": "测试文件夹",
471
- # "parent_id": "parent-456"
472
- # },
473
- # "message": "📤 gRPC 请求: CreateFolder",
474
- # "logger": "file_hub_client.grpc"
475
- # }
476
- ```
477
-
478
- 日志类型包括:
479
- - 📡 初始化日志
480
- - 📤 请求日志(包含请求参数)
481
- - ✅ 响应日志(包含耗时)
482
- - ❌ 错误日志
483
- - 🔗 连接成功
484
- - ⚠️ 连接重试
485
- - 👋 关闭连接
486
-
487
- ### 加载环境变量
488
-
489
- 使用 `python-dotenv` 加载 `.env` 文件(需要额外安装):
490
-
491
- ```bash
492
- pip install python-dotenv
493
- ```
494
-
495
- ```python
496
- from dotenv import load_dotenv
497
- import os
498
-
499
- # 加载 .env 文件
500
- load_dotenv()
501
-
502
- # 现在可以直接使用客户端,它会自动读取环境变量
503
- from file_hub_client import AsyncTamarFileHubClient
504
-
505
- # 示例1:如果 FILE_HUB_PORT 未设置,将使用域名作为完整地址
506
- # .env: FILE_HUB_HOST=api.example.com, FILE_HUB_SECURE=true
507
- async with AsyncTamarFileHubClient() as client:
508
- # 连接到 api.example.com(使用默认的 HTTPS 端口)
509
- pass
510
-
511
- # 示例2:如果 FILE_HUB_PORT 设置了,将使用 host:port 格式
512
- # .env: FILE_HUB_HOST=localhost, FILE_HUB_PORT=50051
513
- async with AsyncTamarFileHubClient() as client:
514
- # 连接到 localhost:50051
515
- pass
516
- ```
517
-
518
- ### 配置优先级
519
-
520
- 客户端配置的优先级如下(从高到低):
521
-
522
- 1. 直接传入的参数
523
- 2. 环境变量
524
- 3. 默认值
525
-
526
- ```python
527
- # 示例:参数会覆盖环境变量
528
- from file_hub_client import AsyncTamarFileHubClient
529
-
530
- # 情况1:覆盖环境变量中的 host
531
- client = AsyncTamarFileHubClient(
532
- host="override-host.com", # 这会覆盖 FILE_HUB_HOST
533
- # port 将使用环境变量 FILE_HUB_PORT(如果设置了)
534
- )
535
-
536
- # 情况2:明确不使用端口(即使环境变量设置了端口)
537
- client = AsyncTamarFileHubClient(
538
- host="api.production.com",
539
- port=None, # 明确指定不使用端口,忽略 FILE_HUB_PORT
540
- secure=True
541
- )
542
- ```
543
-
544
- ## 快速开始
545
-
546
- ### 文件上传
547
-
548
- File Hub Client 提供了统一的上传接口,支持多种上传模式:
549
-
550
- #### 上传模式
551
-
552
- - **NORMAL(普通模式)**:适用于小文件,通过 gRPC 直接上传
553
- - **STREAM(流式上传)**:适用于流式数据上传
554
- - **RESUMABLE(断点续传)**:支持断点续传,适用于大文件和不稳定网络
555
-
556
- #### 最简单的上传
557
-
558
- ```python
559
- from file_hub_client import AsyncTamarFileHubClient
560
-
561
- async with AsyncTamarFileHubClient() as client:
562
- # 设置用户上下文
563
- client.set_user_context(org_id="123", user_id="456")
564
-
565
- # 最简单的用法 - 只需要文件路径
566
- file_info = await client.blobs.upload(
567
- "path/to/document.pdf",
568
- folder_id="1dee0f7b-2e4f-45cd-a462-4e1d82df9bdd" # 上传到指定文件夹,不传则默认文件夹
569
- )
570
- print(f"上传成功: {file_info.file.id}")
571
- print(f"文件类型: {file_info.file.file_type}") # 自动识别为 "pdf"
572
- ```
573
-
574
- #### 从URL上传文件
575
-
576
- ```python
577
- from file_hub_client import AsyncTamarFileHubClient
578
-
579
- async with AsyncTamarFileHubClient() as client:
580
- # 设置用户上下文
581
- client.set_user_context(org_id="123", user_id="456")
582
-
583
- # 从URL下载并上传文件(自动提取文件名)
584
- file_info = await client.blobs.upload(
585
- url="https://example.com/document.pdf"
586
- )
587
- print(f"上传成功: {file_info.file.id}")
588
-
589
- # 从URL上传并指定文件名
590
- file_info = await client.blobs.upload(
591
- url="https://example.com/some-file",
592
- file_name="my_document.pdf" # 指定文件名
593
- )
594
- print(f"文件名: {file_info.file.file_name}")
595
- ```
596
-
597
- #### 上传不同类型的内容
598
-
599
- ```python
600
- from file_hub_client import AsyncTamarFileHubClient
601
- from pathlib import Path
602
-
603
- async with AsyncTamarFileHubClient() as client:
604
- # 1. 上传文件路径(字符串或Path对象)
605
- file_info = await client.blobs.upload("path/to/file.pdf")
606
- file_info = await client.blobs.upload(Path("path/to/file.pdf"))
607
-
608
- # 2. 上传字节数据(需要指定文件名)
609
- content = b"This is file content"
610
- file_info = await client.blobs.upload(
611
- content,
612
- file_name="document.txt"
613
- )
614
-
615
- # 3. 上传文件对象
616
- with open("image.png", "rb") as f:
617
- file_info = await client.blobs.upload(f)
618
- ```
619
-
620
- #### AI生成文件上传(新功能)
621
-
622
- ```python
623
- from file_hub_client import AsyncTamarFileHubClient
624
-
625
- async with AsyncTamarFileHubClient() as client:
626
- # AI模型返回的字节数据(图片、音频、视频等)
627
- # 场景:AI生图模型返回WebP格式图片
628
- ai_image_data = b"\x52\x49\x46\x46...." # WebP格式的字节数据
629
-
630
- # 方式1:显式指定MIME类型(推荐用于AI生成内容)
631
- file_info = await client.blobs.upload(
632
- file=ai_image_data,
633
- mime_type="image/webp" # 明确指定MIME类型
634
- )
635
- print(f"AI生成图片上传成功: {file_info.file.file_name}") # upload_xxx.webp
636
-
637
- # 方式2:自动检测MIME类型(支持26+种格式)
638
- file_info = await client.blobs.upload(file=ai_image_data)
639
- # 系统会自动检测magic bytes并推断为WebP格式
640
-
641
- # 支持的AI生成内容格式:
642
- # 🖼️ 图片: PNG, JPEG, WebP, GIF, BMP等
643
- # 🎵 音频: MP3, WAV, FLAC, AAC, OGG等
644
- # 🎥 视频: MP4, MOV, WebM, AVI等
645
- # 📄 文档: PDF, TXT等
646
- ```
647
-
648
- #### 大文件上传(流式上传和断点续传)
649
-
650
- ```python
651
- from file_hub_client import AsyncTamarFileHubClient, UploadMode
652
-
653
- async with AsyncTamarFileHubClient() as client:
654
- # 设置用户上下文
655
- client.set_user_context(org_id="123", user_id="456")
656
-
657
- # 自动根据文件大小来选择是流式上传还是断点续传
658
- file_info = await client.blobs.upload(
659
- "large_video.mp4",
660
- # mode=UploadMode.RESUMABLE # 也可以手动指定上传的模式
661
- )
662
- ```
663
-
664
- #### 临时文件上传
665
-
666
- ```python
667
- from file_hub_client import AsyncTamarFileHubClient, UploadMode
668
-
669
- async with AsyncTamarFileHubClient() as client:
670
- # 设置用户上下文
671
- client.set_user_context(org_id="123", user_id="456")
672
-
673
- # 自动根据文件大小来选择是流式上传还是断点续传
674
- file_info = await client.blobs.upload(
675
- "large_video.mp4",
676
- # mode=UploadMode.RESUMABLE, # 也可以手动指定上传的模式
677
- is_temporary=True, # 由这个参数指定是否临时文件,是则不会纳入整个文件体系,即用户查询不到这个文件
678
- # expire_seconds, # 过期秒数,默认30天
679
- )
680
- ```
681
-
682
- #### 保留原始文件名上传
683
-
684
- ```python
685
- from file_hub_client import AsyncTamarFileHubClient
686
-
687
- async with AsyncTamarFileHubClient() as client:
688
- # 设置用户上下文
689
- client.set_user_context(org_id="123", user_id="456")
690
-
691
- # 上传时保留原始文件名
692
- file_info = await client.blobs.upload(
693
- "document.pdf",
694
- keep_original_filename=True # 保留原始文件名,默认为False
695
- )
696
-
697
- # 也可以指定文件夹和其他参数
698
- file_info = await client.blobs.upload(
699
- "report.xlsx",
700
- folder_id="folder-123",
701
- keep_original_filename=True, # 保留原始文件名
702
- is_temporary=False
703
- )
704
- ```
705
-
706
- ### 文件下载
707
-
708
- File Hub Client 提供了统一的下载接口,支持两种结构返回:
709
-
710
- #### 下载返回结构
711
-
712
- - **保存到本地(本地路径)**:适用于各种文件,直接下载到本地,分块流式下载,支持重试和断点续传
713
- - **保存到内存(bytes)**:适用于小文件,直接下载到内存,分块流式下载,支持重试
714
-
715
- #### 下载到内存(适用于小文件)
716
-
717
- ```python
718
- from file_hub_client import AsyncTamarFileHubClient
719
-
720
- async with AsyncTamarFileHubClient() as client:
721
- # 设置用户上下文
722
- client.set_user_context(org_id="123", user_id="456")
723
-
724
- # 下载文件到内存(适用于小文件)
725
- content = await client.blobs.download(file_id="file-001")
726
- print(f"下载完成,文件大小: {len(content)} bytes")
727
- ```
728
-
729
- #### 下载到本地文件
730
-
731
- ```python
732
- from file_hub_client import AsyncTamarFileHubClient
733
- from pathlib import Path
734
-
735
- async with AsyncTamarFileHubClient() as client:
736
- # 设置用户上下文
737
- client.set_user_context(org_id="123", user_id="456")
738
-
739
- # 下载文件到本地
740
- save_path = await client.blobs.download(
741
- file_id="file-001",
742
- save_path="downloads/document.pdf" # 或 Path 对象
743
- )
744
- print(f"文件已保存到: {save_path}")
745
- ```
746
-
747
- #### 高级下载功能
748
-
749
- File Hub Client 提供了高级的下载URL管理功能,支持批量操作和直接获取GCS URL:
750
-
751
- ##### 批量生成下载URL
752
-
753
- 当需要为多个文件生成下载URL时,使用批量接口可以显著提高效率:
754
-
755
- ```python
756
- from file_hub_client import AsyncTamarFileHubClient
757
-
758
- async with AsyncTamarFileHubClient() as client:
759
- # 设置用户上下文
760
- client.set_user_context(org_id="123", user_id="456")
761
-
762
- # 批量生成下载URL
763
- file_ids = ["file-001", "file-002", "file-003"]
764
-
765
- result = await client.blobs.batch_generate_download_url(
766
- file_ids=file_ids,
767
- is_cdn=True, # 使用CDN加速(可选,默认为True)
768
- expire_seconds=3600 # URL有效期1小时(可选)
769
- )
770
-
771
- # 处理结果
772
- for url_info in result.download_urls:
773
- if url_info.error:
774
- print(f"文件 {url_info.file_id} 生成URL失败: {url_info.error}")
775
- else:
776
- print(f"文件 {url_info.file_id}:")
777
- print(f" 下载URL: {url_info.url}")
778
- print(f" MIME类型: {url_info.mime_type}")
779
- # 根据MIME类型处理文件
780
- if url_info.mime_type.startswith('image/'):
781
- print(f" 这是一个图片文件")
782
- elif url_info.mime_type == 'application/pdf':
783
- print(f" 这是一个PDF文件")
784
- ```
785
-
786
- 同步客户端示例:
787
-
788
- ```python
789
- from file_hub_client import TamarFileHubClient
790
-
791
- with TamarFileHubClient() as client:
792
- client.set_user_context(org_id="123", user_id="456")
793
-
794
- result = client.blobs.batch_generate_download_url(
795
- file_ids=["file-001", "file-002"],
796
- is_cdn=False # 不使用CDN,直接返回源站URL
797
- )
798
- ```
799
-
800
- **注意**:`batch_generate_download_url` 方法返回一个 `BatchDownloadUrlResponse` 对象,其中 `download_urls` 字段包含 `DownloadUrlInfo` 对象列表,每个对象包含:
801
- - `file_id`: 文件ID
802
- - `url`: 下载URL(如果成功生成)
803
- - `mime_type`: 文件的MIME类型,便于正确处理文件内容
804
- - `error`: 错误信息(如果生成失败)
805
-
806
- ##### 获取GCS URL
807
-
808
- 对于需要直接访问Google Cloud Storage的场景,可以获取文件的GCS URL和MIME类型信息:
809
-
810
- ```python
811
- # 异步客户端 - 获取单个文件的GCS URL
812
- async with AsyncTamarFileHubClient() as client:
813
- client.set_user_context(org_id="123", user_id="456")
814
-
815
- gcs_response = await client.blobs.get_gcs_url(file_id="file-001")
816
- print(f"GCS URL: {gcs_response.gcs_url}")
817
- print(f"MIME类型: {gcs_response.mime_type}")
818
- # 输出示例:
819
- # GCS URL: gs://bucket-name/path/to/file.pdf
820
- # MIME类型: application/pdf
821
- ```
822
-
823
- **注意**:`get_gcs_url` 方法现在返回一个 `GetGcsUrlResponse` 对象,包含:
824
- - `gcs_url`: Google Cloud Storage 的完整路径
825
- - `mime_type`: 文件的MIME类型,便于正确处理文件内容
826
-
827
- ##### 批量获取GCS URL
828
-
829
- 批量获取多个文件的GCS URL和MIME类型信息:
830
-
831
- ```python
832
- # 异步客户端 - 批量获取GCS URL
833
- async with AsyncTamarFileHubClient() as client:
834
- client.set_user_context(org_id="123", user_id="456")
835
-
836
- file_ids = ["file-001", "file-002", "file-003"]
837
- result = await client.blobs.batch_get_gcs_url(file_ids)
838
-
839
- # 处理结果
840
- for url_info in result.gcs_urls:
841
- if url_info.error:
842
- print(f"文件 {url_info.file_id} 获取GCS URL失败: {url_info.error}")
843
- else:
844
- print(f"文件 {url_info.file_id}:")
845
- print(f" GCS URL: {url_info.gcs_url}")
846
- print(f" MIME类型: {url_info.mime_type}")
847
- # 根据MIME类型处理文件
848
- if url_info.mime_type.startswith('image/'):
849
- print(f" 这是一个图片文件")
850
- elif url_info.mime_type == 'application/pdf':
851
- print(f" 这是一个PDF文件")
852
- ```
853
-
854
- 同步客户端示例:
855
-
856
- ```python
857
- # 同步客户端 - 批量获取GCS URL
858
- with TamarFileHubClient() as client:
859
- client.set_user_context(org_id="123", user_id="456")
860
-
861
- # 获取单个GCS URL
862
- gcs_response = client.blobs.get_gcs_url(file_id="file-001")
863
- print(f"GCS URL: {gcs_response.gcs_url}")
864
- print(f"MIME类型: {gcs_response.mime_type}")
865
-
866
- # 批量获取GCS URL
867
- result = client.blobs.batch_get_gcs_url(["file-001", "file-002"])
868
- ```
869
-
870
- ##### 使用场景说明
871
-
872
- 1. **批量下载URL生成**:
873
- - 适用于需要同时下载多个文件的场景
874
- - 支持CDN加速,提供更好的下载体验
875
- - 可设置URL有效期,增强安全性
876
- - 批量操作减少网络往返,提高效率
877
-
878
- 2. **GCS URL获取**:
879
- - 适用于需要与Google Cloud服务集成的场景
880
- - 可用于数据分析、批处理等后端处理
881
- - 支持使用GCS工具进行文件操作
882
- - 返回的是永久有效的存储路径
883
- - 同时返回MIME类型信息,便于正确处理不同类型的文件
884
- - 可根据MIME类型选择合适的处理方式(如图片处理、文档解析等)
885
-
886
- 3. **错误处理**:
887
- - 每个文件独立处理,部分失败不影响其他文件
888
- - 错误信息通过 `error` 字段返回
889
- - 建议在批量操作时做好错误处理和重试逻辑
890
-
891
- ### 媒体文件压缩服务
892
-
893
- File Hub Client 支持图片和视频文件的自动压缩处理,提供多种规格的压缩变体以满足不同使用场景的需求。
894
-
895
- #### 获取文件压缩状态
896
-
897
- ```python
898
- from file_hub_client import AsyncTamarFileHubClient
899
-
900
- async with AsyncTamarFileHubClient() as client:
901
- client.set_user_context(org_id="123", user_id="456")
902
-
903
- # 获取文件压缩状态
904
- status = await client.blobs.get_compression_status(file_id="file-001")
905
-
906
- print(f"压缩状态: {status.status}") # pending, processing, completed, failed
907
- if status.error_message:
908
- print(f"错误信息: {status.error_message}")
909
-
910
- # 查看可用的压缩变体
911
- for variant in status.variants:
912
- print(f"变体: {variant.variant_name}")
913
- print(f" 类型: {variant.variant_type}") # image, video, thumbnail
914
- print(f" 尺寸: {variant.width}x{variant.height}")
915
- print(f" 大小: {variant.file_size} bytes")
916
- print(f" 格式: {variant.format}")
917
- print(f" 压缩比: {variant.compression_ratio:.2f}")
918
- ```
919
-
920
- #### 获取压缩变体列表
921
-
922
- ```python
923
- # 获取所有压缩变体
924
- variants = await client.blobs.get_compressed_variants(file_id="file-001")
925
-
926
- # 按类型过滤变体
927
- image_variants = await client.blobs.get_compressed_variants(
928
- file_id="file-001",
929
- variant_type="image" # image, video, thumbnail
930
- )
931
-
932
- # 处理变体信息
933
- for variant in variants.variants:
934
- print(f"变体名称: {variant.variant_name}") # large, medium, small, thumbnail
935
- print(f"媒体类型: {variant.media_type}")
936
- print(f"文件格式: {variant.format}")
937
- if variant.quality:
938
- print(f"质量: {variant.quality}")
939
- if variant.duration:
940
- print(f"时长: {variant.duration}秒")
941
- if variant.bitrate:
942
- print(f"比特率: {variant.bitrate}")
943
- ```
944
-
945
- #### 下载压缩变体
946
-
947
- ```python
948
- # 生成压缩变体的下载URL
949
- variant_url = await client.blobs.generate_variant_download_url(
950
- file_id="file-001",
951
- variant_name="medium", # large, medium, small, thumbnail
952
- expire_seconds=3600, # URL有效期
953
- is_cdn=True # 是否使用CDN
954
- )
955
-
956
- print(f"下载URL: {variant_url.url}")
957
- if variant_url.error:
958
- print(f"生成URL错误: {variant_url.error}")
959
-
960
- # 查看变体详细信息
961
- if variant_url.variant_info:
962
- info = variant_url.variant_info
963
- print(f"变体信息:")
964
- print(f" 尺寸: {info.width}x{info.height}")
965
- print(f" 格式: {info.format}")
966
- print(f" 文件大小: {info.file_size} bytes")
967
- ```
968
-
969
- #### 触发重新压缩
970
-
971
- ```python
972
- # 触发文件重新压缩(当需要更新压缩设置时)
973
- recompression = await client.blobs.trigger_recompression(
974
- file_id="file-001",
975
- force_reprocess=False # 是否强制重新处理
976
- )
977
-
978
- print(f"任务ID: {recompression.task_id}")
979
- print(f"状态: {recompression.status}")
980
-
981
- # 监控压缩进度
982
- import asyncio
983
- while True:
984
- status = await client.blobs.get_compression_status(file_id="file-001")
985
- print(f"当前状态: {status.status}")
986
-
987
- if status.status in ["completed", "failed"]:
988
- break
989
-
990
- await asyncio.sleep(5) # 等待5秒后再次检查
991
- ```
992
-
993
- #### 同步客户端压缩服务
994
-
995
- ```python
996
- from file_hub_client import TamarFileHubClient
997
-
998
- with TamarFileHubClient() as client:
999
- client.set_user_context(org_id="123", user_id="456")
1000
-
1001
- # 所有压缩服务方法都有对应的同步版本
1002
- status = client.blobs.get_compression_status(file_id="file-001")
1003
- variants = client.blobs.get_compressed_variants(file_id="file-001")
1004
- recompression = client.blobs.trigger_recompression(file_id="file-001")
1005
- variant_url = client.blobs.generate_variant_download_url(
1006
- file_id="file-001",
1007
- variant_name="thumbnail"
1008
- )
1009
- ```
1010
-
1011
- #### 压缩服务使用场景
1012
-
1013
- 1. **多设备适配**:
1014
- - `large` - 高分辨率显示设备
1015
- - `medium` - 标准桌面和平板
1016
- - `small` - 手机端显示
1017
- - `thumbnail` - 缩略图预览
1018
-
1019
- 2. **带宽优化**:
1020
- - 根据网络状况选择合适的变体
1021
- - 移动端使用压缩变体节省流量
1022
- - 预览场景使用缩略图快速加载
1023
-
1024
- 3. **存储优化**:
1025
- - 自动生成多种规格,无需手动处理
1026
- - 智能压缩算法平衡质量和大小
1027
- - 支持视频和图片的不同压缩策略
1028
-
1029
- 4. **性能优化**:
1030
- - 异步压缩处理,不阻塞上传流程
1031
- - 支持重新压缩以应用新的压缩设置
1032
- - 批量状态查询减少网络请求
1033
-
1034
- ### 文件管理操作
1035
-
1036
- File Hub Client 提供了完整的文件管理功能,通过 `files` 服务访问:
1037
-
1038
- #### 获取文件信息
1039
-
1040
- ```python
1041
- from file_hub_client import AsyncTamarFileHubClient
1042
-
1043
- async with AsyncTamarFileHubClient() as client:
1044
- # 设置用户上下文
1045
- client.set_user_context(org_id="123", user_id="456")
1046
-
1047
- # 获取文件详细信息(返回 GetFileResponse 对象)
1048
- response = await client.files.get_file(file_id="file-001")
1049
-
1050
- # 访问文件基本信息
1051
- file_info = response.file
1052
- print(f"文件ID: {file_info.id}")
1053
- print(f"文件名: {file_info.file_name}")
1054
- print(f"文件类型: {file_info.file_type}")
1055
- print(f"创建时间: {file_info.created_at}")
1056
-
1057
- # 访问上传文件详细信息(如果存在)
1058
- if response.upload_file:
1059
- upload_info = response.upload_file
1060
- print(f"文件大小: {upload_info.file_size} bytes")
1061
- print(f"MIME类型: {upload_info.mime_type}")
1062
- print(f"存储类型: {upload_info.storage_type}")
1063
- print(f"存储路径: {upload_info.stored_path}")
1064
- ```
1065
-
1066
- #### 重命名文件
1067
-
1068
- ```python
1069
- # 重命名文件
1070
- updated_file = await client.files.rename_file(
1071
- file_id="file-001",
1072
- new_name="新文档名称.pdf"
1073
- )
1074
- print(f"文件已重命名为: {updated_file.file_name}")
1075
- ```
1076
-
1077
- #### 删除文件
1078
-
1079
- ```python
1080
- # 删除文件
1081
- await client.files.delete_file(file_id="file-001")
1082
- print("文件已删除")
1083
- ```
1084
-
1085
- #### 列出文件
1086
-
1087
- ```python
1088
- # 列出文件夹中的文件
1089
- file_list = await client.files.list_files(
1090
- folder_id="folder-001", # 可选,不指定则列出根目录
1091
- file_name="report", # 可选,按名称过滤
1092
- file_type=["pdf", "docx"], # 可选,按类型过滤
1093
- page_size=20,
1094
- page=1
1095
- )
1096
-
1097
- for file in file_list.files:
1098
- print(f"- {file.file_name} ({file.file_size} bytes)")
1099
- ```
1100
-
1101
- #### 生成分享链接
1102
-
1103
- ```python
1104
- # 生成文件分享链接
1105
- share_id = await client.files.generate_share_link(
1106
- file_id="file-001",
1107
- is_public=True, # 是否公开
1108
- access_scope="view", # 访问权限:view, download
1109
- expire_seconds=86400, # 24小时后过期
1110
- share_password="secret" # 可选,设置访问密码
1111
- )
1112
- print(f"分享ID: {share_id}")
1113
- ```
1114
-
1115
- ### 文件夹操作
1116
-
1117
- File Hub Client 提供了完整的文件夹管理功能,通过 `folders` 服务访问:
1118
-
1119
- #### 创建文件夹
1120
-
1121
- ```python
1122
- from file_hub_client import AsyncTamarFileHubClient
1123
-
1124
- async with AsyncTamarFileHubClient() as client:
1125
- # 设置用户上下文
1126
- client.set_user_context(org_id="123", user_id="456")
1127
-
1128
- # 在根目录创建文件夹
1129
- folder = await client.folders.create_folder(
1130
- folder_name="我的文档"
1131
- )
1132
- print(f"创建文件夹: {folder.id}")
1133
-
1134
- # 在指定文件夹下创建子文件夹
1135
- sub_folder = await client.folders.create_folder(
1136
- folder_name="项目资料",
1137
- parent_id=folder.id
1138
- )
1139
- print(f"创建子文件夹: {sub_folder.id}")
1140
- ```
1141
-
1142
- #### 重命名文件夹
1143
-
1144
- ```python
1145
- # 重命名文件夹
1146
- updated_folder = await client.folders.rename_folder(
1147
- folder_id="folder-001",
1148
- new_name="新文件夹名称"
1149
- )
1150
- print(f"文件夹已重命名为: {updated_folder.folder_name}")
1151
- ```
1152
-
1153
- #### 移动文件夹
1154
-
1155
- ```python
1156
- # 移动文件夹到另一个文件夹下
1157
- moved_folder = await client.folders.move_folder(
1158
- folder_id="folder-001",
1159
- new_parent_id="folder-002" # 目标父文件夹ID
1160
- )
1161
- print(f"文件夹已移动到: {moved_folder.parent_id}")
1162
- ```
1163
-
1164
- #### 删除文件夹
1165
-
1166
- ```python
1167
- # 删除文件夹(包括其中的所有内容)
1168
- await client.folders.delete_folder(folder_id="folder-001")
1169
- print("文件夹已删除")
1170
- ```
1171
-
1172
- #### 列出文件夹
1173
-
1174
- ```python
1175
- # 列出根目录下的文件夹
1176
- folder_list = await client.folders.list_folders()
1177
-
1178
- # 列出指定文件夹下的子文件夹
1179
- sub_folders = await client.folders.list_folders(
1180
- parent_id="folder-001",
1181
- folder_name="项目", # 可选,按名称过滤
1182
- )
1183
-
1184
- for folder in folder_list.items:
1185
- print(f"- {folder.folder_name} (ID: {folder.id})")
1186
- print(f" 创建者: {folder.created_by}")
1187
- print(f" 创建时间: {folder.created_at}")
1188
- ```
1189
-
1190
- #### 完整示例:组织文件结构
1191
-
1192
- ```python
1193
- from file_hub_client import AsyncTamarFileHubClient
1194
-
1195
- async with AsyncTamarFileHubClient() as client:
1196
- # 设置用户上下文
1197
- client.set_user_context(org_id="123", user_id="456")
1198
-
1199
- # 创建项目文件夹结构
1200
- project_folder = await client.folders.create_folder("我的项目")
1201
- docs_folder = await client.folders.create_folder("文档", parent_id=project_folder.id)
1202
- images_folder = await client.folders.create_folder("图片", parent_id=project_folder.id)
1203
-
1204
- # 上传文件到对应文件夹
1205
- doc_file = await client.blobs.upload(
1206
- "project_plan.pdf",
1207
- folder_id=docs_folder.id
1208
- )
1209
-
1210
- image_file = await client.blobs.upload(
1211
- "logo.png",
1212
- folder_id=images_folder.id
1213
- )
1214
-
1215
- # 列出项目文件夹的内容
1216
- print("项目结构:")
1217
-
1218
- # 列出子文件夹
1219
- folders = await client.folders.list_folders(parent_id=project_folder.id)
1220
- for folder in folders.items:
1221
- print(f"📁 {folder.folder_name}/")
1222
-
1223
- # 列出每个文件夹中的文件
1224
- files = await client.files.list_files(folder_id=folder.id)
1225
- for file in files.files:
1226
- print(f" 📄 {file.file_name}")
1227
- ```
1228
-
1229
- ### Taple 电子表格操作
1230
-
1231
- File Hub Client 提供了完整的类 Excel 电子表格功能,通过 `taples` 服务访问。支持表格、工作表、列、行、单元格的完整管理功能。
1232
-
1233
- #### 基本操作
1234
-
1235
- ```python
1236
- from file_hub_client import AsyncTamarFileHubClient
1237
-
1238
- async with AsyncTamarFileHubClient() as client:
1239
- # 设置用户上下文
1240
- client.set_user_context(org_id="123", user_id="456")
1241
-
1242
- # 创建表格
1243
- table = await client.taples.create_table(
1244
- name="员工信息表",
1245
- folder_id="folder-123", # 可选,不指定则使用默认文件夹
1246
- description="公司员工基本信息"
1247
- )
1248
-
1249
- # 创建工作表
1250
- sheet = await client.taples.create_sheet(
1251
- table_id=table.table.id,
1252
- name="基本信息",
1253
- description="员工基本信息工作表"
1254
- )
1255
-
1256
- # 获取表格信息
1257
- table_info = await client.taples.get_table(table_id=table.table.id)
1258
- # 或通过文件ID获取
1259
- # table_info = await client.taples.get_table(file_id="file-123")
1260
- ```
1261
-
1262
- #### 列、行、单元格操作
1263
-
1264
- ```python
1265
- async with AsyncTamarFileHubClient() as client:
1266
- client.set_user_context(org_id="123", user_id="456")
1267
-
1268
- # 创建列(支持幂等性)
1269
- column = await client.taples.create_column(
1270
- sheet_id=sheet.sheet.id,
1271
- name="姓名",
1272
- column_type="text",
1273
- width=200,
1274
- idempotency_key="create-column-name-001"
1275
- )
1276
-
1277
- # 更新列
1278
- updated_column = await client.taples.update_column(
1279
- sheet_id=sheet.sheet.id,
1280
- column_key=column.column.column_key,
1281
- name="员工姓名",
1282
- width=250,
1283
- hidden=False
1284
- )
1285
-
1286
- # 创建行(支持幂等性)
1287
- row = await client.taples.create_row(
1288
- sheet_id=sheet.sheet.id,
1289
- position=0, # 可选,指定位置
1290
- height=30, # 可选,行高
1291
- hidden=False, # 可选,是否隐藏
1292
- idempotency_key="create-row-001"
1293
- )
1294
-
1295
- # 编辑单元格(支持幂等性)
1296
- cell = await client.taples.edit_cell(
1297
- sheet_id=sheet.sheet.id,
1298
- column_key=column.column.column_key,
1299
- row_key=row.row.row_key,
1300
- raw_value="张三",
1301
- idempotency_key="edit-cell-001"
1302
- )
1303
-
1304
- # 删除操作
1305
- await client.taples.delete_cell(sheet_id=sheet.sheet.id, column_key="col_1", row_key="row_1")
1306
- await client.taples.delete_row(sheet_id=sheet.sheet.id, row_key="row_1")
1307
- await client.taples.delete_column(sheet_id=sheet.sheet.id, column_key="col_1")
1308
- ```
1309
-
1310
- #### 批量操作
1311
-
1312
- ```python
1313
- # 批量编辑列
1314
- column_operations = [
1315
- {
1316
- "create": {
1317
- "name": "部门",
1318
- "column_type": "text",
1319
- "position": 1
1320
- }
1321
- },
1322
- {
1323
- "update": {
1324
- "column_key": "col_1",
1325
- "name": "新名称",
1326
- "width": 300
1327
- }
1328
- },
1329
- {
1330
- "delete": {
1331
- "column_key": "col_2"
1332
- }
1333
- }
1334
- ]
1335
-
1336
- result = await client.taples.batch_edit_columns(
1337
- sheet_id=sheet.sheet.id,
1338
- operations=column_operations,
1339
- idempotency_key="batch-columns-001"
1340
- )
1341
-
1342
- # 批量编辑行
1343
- row_operations = [
1344
- {
1345
- "create": {
1346
- "position": 0,
1347
- "height": 40
1348
- }
1349
- },
1350
- {
1351
- "update": {
1352
- "row_key": "row_1",
1353
- "height": 50,
1354
- "hidden": True
1355
- }
1356
- }
1357
- ]
1358
-
1359
- result = await client.taples.batch_edit_rows(
1360
- sheet_id=sheet.sheet.id,
1361
- operations=row_operations
1362
- )
1363
-
1364
- # 批量编辑单元格
1365
- cell_operations = [
1366
- {
1367
- "edit": {
1368
- "column_key": "col_1",
1369
- "row_key": "row_1",
1370
- "raw_value": "销售部"
1371
- }
1372
- },
1373
- {
1374
- "clear": {
1375
- "column_key": "col_2",
1376
- "row_key": "row_1"
1377
- }
1378
- }
1379
- ]
1380
-
1381
- result = await client.taples.batch_edit_cells(
1382
- sheet_id=sheet.sheet.id,
1383
- operations=cell_operations
1384
- )
1385
- ```
1386
-
1387
- #### 数据获取
1388
-
1389
- ```python
1390
- # 获取工作表版本(轻量级)
1391
- version_info = await client.taples.get_sheet_version(sheet_id=sheet.sheet.id)
1392
- print(f"当前版本: {version_info.version}")
1393
-
1394
- # 获取完整工作表数据
1395
- sheet_data = await client.taples.get_sheet_data(
1396
- sheet_id=sheet.sheet.id,
1397
- version=100 # 可选,获取从该版本以来的变化
1398
- )
1399
-
1400
- # 获取列数据(包含该列所有单元格)
1401
- column_data = await client.taples.get_column_data(
1402
- sheet_id=sheet.sheet.id,
1403
- column_key="col_1"
1404
- )
1405
-
1406
- # 获取行数据(包含该行所有单元格)
1407
- row_data = await client.taples.get_row_data(
1408
- sheet_id=sheet.sheet.id,
1409
- row_key="row_1"
1410
- )
1411
-
1412
- # 获取单个单元格数据
1413
- cell_data = await client.taples.get_cell_data(
1414
- sheet_id=sheet.sheet.id,
1415
- column_key="col_1",
1416
- row_key="row_1"
1417
- )
1418
- ```
1419
-
1420
- #### 版本控制和冲突处理
1421
-
1422
- Taple 支持乐观锁版本控制,在并发编辑时自动处理版本冲突:
1423
-
1424
- ```python
1425
- # 方式1:自动获取版本(推荐)
1426
- # SDK 会自动获取最新版本号
1427
- column = await client.taples.create_column(
1428
- sheet_id=sheet.sheet.id,
1429
- name="自动版本",
1430
- column_type="text"
1431
- )
1432
-
1433
- # 方式2:手动指定版本
1434
- # 适用于需要精确控制的场景
1435
- version_info = await client.taples.get_sheet_version(sheet_id=sheet.sheet.id)
1436
- column = await client.taples.create_column(
1437
- sheet_id=sheet.sheet.id,
1438
- name="手动版本",
1439
- column_type="text",
1440
- sheet_version=version_info.version,
1441
- client_id="my-client-123"
1442
- )
1443
-
1444
- # 批量操作时的版本控制
1445
- operations = [...] # 你的操作列表
1446
- batch_result = await client.taples.batch_edit_sheet(
1447
- sheet_id=sheet.sheet.id,
1448
- operations=operations,
1449
- sheet_version=version_info.version,
1450
- client_id="my-client-123"
1451
- )
1452
-
1453
- # 检查冲突
1454
- if batch_result.conflict_info and batch_result.conflict_info.has_conflict:
1455
- print(f"版本冲突: {batch_result.conflict_info.conflict_type}")
1456
- print(f"服务器版本: {batch_result.conflict_info.server_version}")
1457
- ```
1458
-
1459
- #### 数据导入
1460
-
1461
- Taple 支持从 CSV、Excel 等文件导入数据:
1462
-
1463
- ```python
1464
- import tempfile
1465
- import csv
1466
-
1467
- # 创建测试 CSV 文件
1468
- def create_test_csv():
1469
- temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False, encoding='utf-8')
1470
- writer = csv.writer(temp_file)
1471
- writer.writerow(['姓名', '年龄', '部门', '薪资'])
1472
- writer.writerow(['张三', '28', '技术部', '15000'])
1473
- writer.writerow(['李四', '32', '销售部', '12000'])
1474
- temp_file.close()
1475
- return temp_file.name
1476
-
1477
- # 上传 CSV 文件
1478
- csv_file = create_test_csv()
1479
- upload_result = await client.blobs.upload(csv_file, folder_id=folder_id)
1480
-
1481
- # 导入到表格
1482
- import_result = await client.taples.import_table_data(
1483
- table_id=table.table.id,
1484
- file_id=upload_result.file.id,
1485
- import_mode="append", # append 或 overwrite
1486
- sheet_index=0, # 导入到第几个工作表
1487
- has_header=True, # 第一行是否为表头
1488
- idempotency_key="import-csv-001"
1489
- )
1490
-
1491
- if import_result.success:
1492
- print(f"导入成功!")
1493
- print(f"导入了 {import_result.rows_imported} 行数据")
1494
- print(f"创建了 {import_result.columns_created} 列")
1495
- else:
1496
- print(f"导入失败: {import_result.error_message}")
1497
- ```
1498
-
1499
- #### 数据导出
1500
-
1501
- Taple 支持导出为多种格式:
1502
-
1503
- ```python
1504
- from file_hub_client.enums import ExportFormat
1505
-
1506
- # 导出为 Excel
1507
- export_result = await client.taples.export_table_data(
1508
- table_id=table.table.id,
1509
- format=ExportFormat.XLSX,
1510
- options={
1511
- "include_formulas": True,
1512
- "include_styles": True,
1513
- "include_hidden_sheets": False,
1514
- "include_hidden_rows_cols": False
1515
- },
1516
- idempotency_key="export-excel-001"
1517
- )
1518
-
1519
- if export_result.success:
1520
- print(f"导出成功!")
1521
- print(f"文件ID: {export_result.file_id}")
1522
- print(f"文件名: {export_result.file_name}")
1523
- print(f"下载链接: {export_result.download_url}")
1524
-
1525
- # 下载导出的文件
1526
- await client.blobs.download(
1527
- file_id=export_result.file_id,
1528
- save_path=f"exports/{export_result.file_name}"
1529
- )
1530
-
1531
- # 支持的导出格式
1532
- # - ExportFormat.XLSX: Excel 格式
1533
- # - ExportFormat.CSV: CSV 格式(多工作表会生成 ZIP)
1534
- # - ExportFormat.JSON: JSON 格式
1535
- # - ExportFormat.HTML: HTML 表格
1536
- # - ExportFormat.MARKDOWN: Markdown 表格
1537
- ```
1538
-
1539
- #### 表格克隆操作
1540
-
1541
- Taple 支持将表格数据克隆到另一个组织,包括所有工作表、列、行和单元格数据:
1542
-
1543
- ```python
1544
- from file_hub_client import AsyncTamarFileHubClient
1545
- import uuid
1546
-
1547
- async def clone_table_example():
1548
- async with AsyncTamarFileHubClient() as client:
1549
- client.set_user_context(org_id="source-org-123", user_id="456")
1550
-
1551
- # 克隆表格到另一个组织
1552
- clone_result = await client.taples.clone_table_data(
1553
- source_table_id="table-123",
1554
- target_org_id="target-org-456",
1555
- target_user_id="target-user-789",
1556
- target_folder_id="target-folder-001", # 可选,目标文件夹
1557
- new_table_name="克隆的员工表_2024", # 可选,新表格名称
1558
- include_views=False, # 是否包含视图数据
1559
- idempotency_key=str(uuid.uuid4()) # 幂等性键
1560
- )
1561
-
1562
- if clone_result.success:
1563
- print(f"克隆成功!")
1564
- print(f"新表格ID: {clone_result.new_table_id}")
1565
- print(f"新文件ID: {clone_result.new_file_id}")
1566
- print(f"克隆了 {clone_result.sheets_cloned} 个工作表")
1567
- print(f"克隆了 {clone_result.cells_cloned} 个单元格")
1568
- print(f"创建时间: {clone_result.created_at}")
1569
- else:
1570
- print(f"克隆失败: {clone_result.error_message}")
1571
-
1572
- # 运行示例
1573
- import asyncio
1574
- asyncio.run(clone_table_example())
1575
- ```
1576
-
1577
- **同步客户端示例**:
1578
-
1579
- ```python
1580
- from file_hub_client import TamarFileHubClient
1581
- import uuid
1582
-
1583
- with TamarFileHubClient() as client:
1584
- client.set_user_context(org_id="source-org-123", user_id="456")
1585
-
1586
- # 克隆表格(最简示例)
1587
- clone_result = client.taples.clone_table_data(
1588
- source_table_id="table-123",
1589
- target_org_id="target-org-456",
1590
- target_user_id="target-user-789"
1591
- # 其他参数都是可选的
1592
- )
1593
-
1594
- print(f"克隆结果: {clone_result.success}")
1595
- if clone_result.success:
1596
- print(f"新表格ID: {clone_result.new_table_id}")
1597
- ```
1598
-
1599
- **克隆操作特点**:
1600
-
1601
- - **跨组织克隆**:可以将表格从一个组织克隆到另一个组织
1602
- - **完整数据复制**:包括表格结构、工作表、列定义、行数据和单元格内容
1603
- - **可选视图数据**:通过 `include_views` 参数控制是否包含视图数据(默认不包含)
1604
- - **灵活命名**:可自定义新表格名称,不指定则自动使用原名称+Copy后缀
1605
- - **目标位置控制**:可指定目标文件夹,不指定则使用目标用户的默认文件夹
1606
- - **幂等性支持**:支持幂等性键,避免重复克隆
1607
-
1608
- #### 完整示例:创建和填充数据表
1609
-
1610
- ```python
1611
- from file_hub_client import AsyncTamarFileHubClient
1612
- from datetime import datetime
1613
- import uuid
1614
-
1615
- async def create_employee_table():
1616
- async with AsyncTamarFileHubClient() as client:
1617
- client.set_user_context(org_id="123", user_id="456")
1618
-
1619
- # 1. 创建表格
1620
- table = await client.taples.create_table(
1621
- name=f"员工信息_{datetime.now().strftime('%Y%m%d')}",
1622
- description="员工基本信息管理表",
1623
- idempotency_key=str(uuid.uuid4())
1624
- )
1625
-
1626
- # 2. 创建工作表
1627
- sheet = await client.taples.create_sheet(
1628
- table_id=table.table.id,
1629
- name="花名册",
1630
- description="员工花名册"
1631
- )
1632
-
1633
- # 3. 批量创建列
1634
- column_operations = [
1635
- {"create": {"name": "工号", "column_type": "text", "position": 0}},
1636
- {"create": {"name": "姓名", "column_type": "text", "position": 1}},
1637
- {"create": {"name": "部门", "column_type": "text", "position": 2}},
1638
- {"create": {"name": "入职日期", "column_type": "date", "position": 3}},
1639
- {"create": {"name": "薪资", "column_type": "number", "position": 4}}
1640
- ]
1641
-
1642
- columns_result = await client.taples.batch_edit_columns(
1643
- sheet_id=sheet.sheet.id,
1644
- operations=column_operations
1645
- )
1646
-
1647
- # 4. 批量创建行并填充数据
1648
- employees = [
1649
- {"工号": "E001", "姓名": "张三", "部门": "技术部", "入职日期": "2023-01-15", "薪资": "15000"},
1650
- {"工号": "E002", "姓名": "李四", "部门": "销售部", "入职日期": "2023-03-20", "薪资": "12000"},
1651
- {"工号": "E003", "姓名": "王五", "部门": "市场部", "入职日期": "2023-06-10", "薪资": "13000"}
1652
- ]
1653
-
1654
- # 创建行
1655
- row_operations = [{"create": {"position": i}} for i in range(len(employees))]
1656
- rows_result = await client.taples.batch_edit_rows(
1657
- sheet_id=sheet.sheet.id,
1658
- operations=row_operations
1659
- )
1660
-
1661
- # 填充数据
1662
- cell_operations = []
1663
- for i, (row, employee) in enumerate(zip(rows_result['results'], employees)):
1664
- if row['success'] and row['row']:
1665
- row_key = row['row'].row_key
1666
- for j, (col, (field, value)) in enumerate(zip(columns_result['results'], employee.items())):
1667
- if col['success'] and col['column']:
1668
- cell_operations.append({
1669
- "edit": {
1670
- "column_key": col['column'].column_key,
1671
- "row_key": row_key,
1672
- "raw_value": value
1673
- }
1674
- })
1675
-
1676
- # 批量更新单元格
1677
- await client.taples.batch_edit_cells(
1678
- sheet_id=sheet.sheet.id,
1679
- operations=cell_operations
1680
- )
1681
-
1682
- print(f"表格创建成功!")
1683
- print(f"表格ID: {table.table.id}")
1684
- print(f"工作表ID: {sheet.sheet.id}")
1685
-
1686
- # 5. 读取数据验证
1687
- sheet_data = await client.taples.get_sheet_data(sheet_id=sheet.sheet.id)
1688
- print(f"数据行数: {len(sheet_data.rows)}")
1689
- print(f"数据列数: {len(sheet_data.columns)}")
1690
-
1691
- # 运行示例
1692
- import asyncio
1693
- asyncio.run(create_employee_table())
1694
- ```
1695
-
1696
- #### 高级功能:合并单元格
1697
-
1698
- ```python
1699
- # 合并单元格
1700
- merge_result = await client.taples.merge_cells(
1701
- sheet_id=sheet.sheet.id,
1702
- start_row_key="row_1",
1703
- end_row_key="row_3",
1704
- start_column_key="col_1",
1705
- end_column_key="col_2",
1706
- idempotency_key="merge-cells-001"
1707
- )
1708
-
1709
- # 取消合并
1710
- unmerge_result = await client.taples.unmerge_cells(
1711
- sheet_id=sheet.sheet.id,
1712
- merged_cell_id=merge_result.merged_cell.id,
1713
- idempotency_key="unmerge-cells-001"
1714
- )
1715
-
1716
- # 获取合并单元格信息
1717
- merged_cells = await client.taples.list_merged_cells(sheet_id=sheet.sheet.id)
1718
- for cell in merged_cells.merged_cells:
1719
- print(f"合并区域: {cell.start_row_key}-{cell.end_row_key}, {cell.start_column_key}-{cell.end_column_key}")
1720
- ```
1721
-
1722
- #### 高级功能:表格视图
1723
-
1724
- ```python
1725
- # 创建视图
1726
- view = await client.taples.create_table_view(
1727
- table_id=table.table.id,
1728
- name="销售部视图",
1729
- description="只显示销售部数据",
1730
- filter_config={
1731
- "conditions": [
1732
- {
1733
- "column_key": "col_dept",
1734
- "operator": "equals",
1735
- "value": "销售部"
1736
- }
1737
- ]
1738
- },
1739
- sort_config={
1740
- "rules": [
1741
- {
1742
- "column_key": "col_salary",
1743
- "order": "desc"
1744
- }
1745
- ]
1746
- },
1747
- visible_columns=["col_name", "col_dept", "col_salary"],
1748
- idempotency_key="create-view-001"
1749
- )
1750
-
1751
- # 获取视图列表
1752
- views = await client.taples.list_table_views(table_id=table.table.id)
1753
- for v in views.views:
1754
- print(f"视图: {v.name} - {v.description}")
1755
-
1756
- # 使用视图获取数据
1757
- view_data = await client.taples.get_table_view_data(
1758
- view_id=view.view.id,
1759
- page_size=20,
1760
- page=1
1761
- )
1762
- ```
1763
-
1764
- #### 同步客户端示例
1765
-
1766
- 所有异步操作都有对应的同步版本:
1767
-
1768
- ```python
1769
- from file_hub_client import TamarFileHubClient
1770
-
1771
- with TamarFileHubClient() as client:
1772
- client.set_user_context(org_id="123", user_id="456")
1773
-
1774
- # 创建表格
1775
- table = client.taples.create_table(
1776
- name="销售数据",
1777
- description="2024年销售数据"
1778
- )
1779
-
1780
- # 创建工作表
1781
- sheet = client.taples.create_sheet(
1782
- table_id=table.table.id,
1783
- name="Q1数据"
1784
- )
1785
-
1786
- # 创建列
1787
- column = client.taples.create_column(
1788
- sheet_id=sheet.sheet.id,
1789
- name="产品名称",
1790
- column_type="text"
1791
- )
1792
-
1793
- print(f"创建成功: {table.table.id}")
1794
- ```
1795
-
1796
- ### 最简单的使用方式(推荐)
1797
-
1798
- File Hub Client 提供了预配置的单例客户端,可以直接导入使用:
1799
-
1800
- ```python
1801
- # 同步客户端
1802
- import os
1803
- from file_hub_client import tamar_client as client
1804
-
1805
- # 直接使用,无需 with 语句
1806
- client.set_user_context(org_id="123", user_id="456")
1807
- file_path = os.path.abspath("1.jpg")
1808
- file_info = client.blobs.upload(file_path)
1809
- ```
1810
-
1811
- ```python
1812
- # 异步客户端
1813
- import asyncio
1814
- import os
1815
- from file_hub_client import async_tamar_client as async_client
1816
-
1817
-
1818
- async def main():
1819
- # 直接使用,无需 with 语句
1820
- await async_client._ensure_connected() # 需要手动连接
1821
- async_client.set_user_context(org_id="123", user_id="456")
1822
- file_path = os.path.abspath("1.jpg")
1823
- file_info = await async_client.blobs.upload(file_path)
1824
- print(f"上传成功: {file_info.file.id}")
1825
-
1826
-
1827
- asyncio.run(main())
1828
- ```
1829
-
1830
- ### 自定义配置的单例
1831
-
1832
- 如果需要自定义配置,可以使用 `get_client()` 或 `get_async_client()`:
1833
-
1834
- ```python
1835
- from file_hub_client import get_client
1836
-
1837
- # 获取自定义配置的客户端(单例)
1838
- client = get_client(
1839
- host="custom-server.com",
1840
- port=50051,
1841
- secure=True
1842
- )
1843
- ```
1844
-
1845
- ### 使用上下文管理器(可选)
1846
-
1847
- 如果您希望明确控制连接的生命周期,仍然可以使用上下文管理器:
1848
-
1849
- ```python
1850
- import os
1851
- from file_hub_client import TamarFileHubClient
1852
-
1853
- # 使用 with 语句
1854
- with TamarFileHubClient(host="localhost", port=50051) as client:
1855
- file_path = os.path.abspath("1.jpg")
1856
- file_info = client.blobs.upload(file_path)
1857
- ```
1858
-
1859
- ### 异步客户端示例
1860
-
1861
- ```python
1862
- import asyncio
1863
- import os
1864
- from file_hub_client import AsyncTamarFileHubClient
1865
-
1866
-
1867
- async def main():
1868
- # 创建客户端
1869
- async with AsyncTamarFileHubClient(host="localhost", port=50051) as client:
1870
- # 上传文件
1871
- file_path = os.path.abspath("1.jpg")
1872
- file_info = await client.blobs.upload(file_path)
1873
- print(f"上传成功: {file_info.file.id}")
1874
-
1875
-
1876
- asyncio.run(main())
1877
- ```
1878
-
1879
- ### 同步客户端示例
1880
-
1881
- ```python
1882
- import os
1883
- from file_hub_client import TamarFileHubClient
1884
-
1885
- # 创建客户端
1886
- with TamarFileHubClient(host="localhost", port=50051) as client:
1887
- # 上传文件
1888
- file_path = os.path.abspath("1.jpg")
1889
- file_info = client.blobs.upload(file_path)
1890
- print(f"上传成功: {file_info.file.id}")
1891
- ```
1892
-
1893
- ### 使用用户上下文
1894
-
1895
- File Hub Client 支持精细的用户上下文管理,区分资源所有权和实际操作者:
1896
-
1897
- ```python
1898
- import os
1899
- from file_hub_client import AsyncTamarFileHubClient, UserContext, RequestContext, Role
1900
-
1901
- # 创建用户上下文
1902
- user_context = UserContext(
1903
- org_id="org-123", # 组织ID
1904
- user_id="user-456", # 用户ID(资源所有者)
1905
- role=Role.ACCOUNT, # 角色:ACCOUNT, AGENT, SYSTEM
1906
- actor_id="agent-789" # 实际操作者ID(可选,默认为user_id)
1907
- )
1908
-
1909
- # 创建请求上下文(自动收集客户端信息)
1910
- request_context = RequestContext(
1911
- client_ip="192.168.1.100", # 客户端IP(可选)
1912
- client_type="web", # 客户端类型:web, mobile, desktop, cli
1913
- client_version="2.0.0", # 客户端版本
1914
- extra={"session_id": "xyz"} # 额外的元数据
1915
- )
1916
-
1917
- # 使用上下文创建客户端
1918
- async with AsyncTamarFileHubClient(
1919
- user_context=user_context,
1920
- request_context=request_context
1921
- ) as client:
1922
- # 所有操作都会包含上下文信息
1923
- file_path = os.path.abspath("1.jpg")
1924
- await client.blobs.upload(file_path)
1925
- ```
1926
-
1927
- ### 动态切换用户上下文
1928
-
1929
- ```python
1930
- from file_hub_client import tamar_client as client, Role
1931
-
1932
- # 初始用户
1933
- client.set_user_context(
1934
- org_id="123",
1935
- user_id="456",
1936
- role=Role.ACCOUNT
1937
- )
1938
- ```
1939
-
1940
- ### 请求追踪
1941
-
1942
- 客户端会自动生成请求ID并收集环境信息:
1943
-
1944
- ```python
1945
- from file_hub_client import tamar_client as client
1946
-
1947
- # 获取当前上下文信息
1948
- user_ctx = client.get_user_context()
1949
- request_ctx = client.get_request_context()
1950
-
1951
- print(f"请求ID: {request_ctx.request_id}")
1952
- print(f"客户端信息: {request_ctx.client_type} v{request_ctx.client_version}")
1953
- print(f"操作者: {user_ctx.actor_id} (角色: {user_ctx.role})")
1954
- ```
1955
-
1956
- ### 显式请求ID控制
1957
-
1958
- 所有服务方法都支持显式传入 `request_id` 参数,用于更精确的请求追踪和调试:
1959
-
1960
- ```python
1961
- from file_hub_client import AsyncTamarFileHubClient
1962
- import uuid
1963
-
1964
- # 创建客户端
1965
- client = AsyncTamarFileHubClient(user_context=user_context)
1966
-
1967
- # 方式1:不传入request_id,系统自动生成
1968
- table = await client.taples.create_table(name="auto_request_id_table")
1969
-
1970
- # 方式2:传入自定义request_id
1971
- custom_request_id = f"create-table-{uuid.uuid4().hex}"
1972
- table = await client.taples.create_table(
1973
- name="custom_request_id_table",
1974
- request_id=custom_request_id
1975
- )
1976
-
1977
- # 方式3:使用业务相关的request_id
1978
- business_request_id = "user-action-2025-0714-001"
1979
- folder = await client.folders.create_folder(
1980
- folder_name="important_folder",
1981
- request_id=business_request_id
1982
- )
1983
-
1984
- # 同步客户端同样支持
1985
- sync_client = TamarFileHubClient(user_context=user_context)
1986
- response = sync_client.files.get_file(
1987
- file_id="file-123",
1988
- request_id="debug-get-file-001"
1989
- )
1990
- # response.file 包含文件基本信息
1991
- # response.upload_file 包含上传文件详细信息(可能为None)
1992
- ```
1993
-
1994
- #### 请求ID优先级
1995
-
1996
- 请求ID的使用优先级如下:
1997
-
1998
- 1. **显式传入的 request_id 参数**(最高优先级)
1999
- 2. **RequestContext 中的 request_id**
2000
- 3. **自动生成的 UUID**(默认行为)
2001
-
2002
- ```python
2003
- # 优先级示例
2004
- request_context = RequestContext(
2005
- extra={"request_id": "context-request-id-123"}
2006
- )
2007
-
2008
- client = AsyncTamarFileHubClient(
2009
- user_context=user_context,
2010
- request_context=request_context
2011
- )
2012
-
2013
- # 使用显式传入的request_id(优先级最高)
2014
- await client.taples.create_table(
2015
- name="explicit_priority",
2016
- request_id="explicit-request-id-456"
2017
- )
2018
- # 实际使用:explicit-request-id-456
2019
-
2020
- # 使用RequestContext中的request_id
2021
- await client.taples.create_table(name="context_priority")
2022
- # 实际使用:context-request-id-123
2023
-
2024
- # 如果都没有,自动生成UUID
2025
- minimal_client = AsyncTamarFileHubClient(user_context=user_context)
2026
- await minimal_client.taples.create_table(name="auto_generated")
2027
- # 实际使用:自动生成的UUID
2028
- ```
2029
-
2030
- #### 支持request_id的服务方法
2031
-
2032
- 所有服务方法都支持 `request_id` 参数:
2033
-
2034
- **Taple 服务**:
2035
- - `create_table()`, `get_table()`, `delete_table()`
2036
- - `create_sheet()`, `get_sheet()`, `delete_sheet()`
2037
- - `create_column()`, `update_column()`, `delete_column()`
2038
- - `create_row()`, `update_row()`, `delete_row()`
2039
- - `edit_cell()`, `clear_cell()`, `get_cell_data()`
2040
- - `import_table_data()`, `export_table_data()`
2041
- - `clone_table_data()`
2042
- - 所有批量操作方法
2043
-
2044
- **文件服务**:
2045
- - `get_file()` - 返回 `GetFileResponse` 对象,包含 `file` 和 `upload_file` 信息
2046
- - `rename_file()`, `delete_file()`, `list_files()`
2047
- - `generate_share_link()`, `visit_file()`
2048
-
2049
- **文件夹服务**:
2050
- - `create_folder()`, `rename_folder()`, `move_folder()`, `delete_folder()`
2051
- - `list_folders()`
2052
-
2053
- **Blob 服务**:
2054
- - `upload()`, `download()`, `generate_upload_url()`, `generate_download_url()`
2055
-
2056
- #### 请求追踪最佳实践
2057
-
2058
- 1. **业务操作使用有意义的request_id**:
2059
- ```python
2060
- # 用户触发的操作
2061
- request_id = f"user-{user_id}-create-table-{int(time.time())}"
2062
-
2063
- # 定时任务
2064
- request_id = f"cron-export-{datetime.now().strftime('%Y%m%d_%H%M%S')}"
2065
-
2066
- # 批量操作
2067
- request_id = f"batch-import-{batch_id}"
2068
- ```
2069
-
2070
- 2. **调试时使用描述性request_id**:
2071
- ```python
2072
- # 调试特定功能
2073
- request_id = "debug-column-creation-issue"
2074
-
2075
- # 性能测试
2076
- request_id = f"perf-test-{operation_name}-{iteration}"
2077
- ```
2078
-
2079
- 3. **生产环境保持简洁**:
2080
- ```python
2081
- # 生产环境可以使用简短的标识符
2082
- request_id = f"prod-{uuid.uuid4().hex[:8]}"
2083
- ```
2084
-
2085
- ## 高级功能
2086
-
2087
- ### 幂等性支持
2088
-
2089
- 许多操作支持幂等性,通过 `idempotency_key` 参数防止重复操作:
2090
-
2091
- ```python
2092
- from file_hub_client import AsyncTamarFileHubClient, generate_idempotency_key
2093
-
2094
- async with AsyncTamarFileHubClient() as client:
2095
- # 自动生成幂等性键
2096
- key = generate_idempotency_key("create", "table", "employee_2024")
2097
-
2098
- # 使用幂等性键创建表格
2099
- # 即使多次调用,也只会创建一次
2100
- table = await client.taples.create_table(
2101
- name="员工表_2024",
2102
- idempotency_key=key
2103
- )
2104
-
2105
- # 使用 IdempotencyManager 管理多个操作
2106
- from file_hub_client import IdempotencyManager
2107
-
2108
- manager = IdempotencyManager(prefix="import_batch_001")
2109
-
2110
- # 批量导入,每个操作都有唯一的幂等性键
2111
- for i, file_id in enumerate(file_ids):
2112
- await client.taples.import_table_data(
2113
- table_id=table.table.id,
2114
- file_id=file_id,
2115
- idempotency_key=manager.generate_key("import", str(i))
2116
- )
2117
- ```
2118
-
2119
- ### 错误重试机制
2120
-
2121
- SDK 内置了智能的错误重试机制:
2122
-
2123
- ```python
2124
- from file_hub_client import AsyncTamarFileHubClient
2125
-
2126
- # 配置重试策略
2127
- client = AsyncTamarFileHubClient(
2128
- retry_count=5, # 最大重试次数
2129
- retry_delay=1.0 # 初始重试延迟(秒)
2130
- )
2131
-
2132
- # 使用装饰器自定义重试逻辑
2133
- from file_hub_client.utils.retry import retry_with_backoff
2134
-
2135
- @retry_with_backoff(max_retries=3, base_delay=0.5)
2136
- async def upload_with_retry(client, file_path):
2137
- return await client.blobs.upload(file_path)
2138
- ```
2139
-
2140
- ### 批量操作优化
2141
-
2142
- 对于大量数据操作,使用批量接口可以显著提高性能:
2143
-
2144
- ```python
2145
- # 批量操作示例
2146
- async def batch_import_data(client, sheet_id, data_rows):
2147
- # 分批处理,每批100行
2148
- batch_size = 100
2149
-
2150
- for i in range(0, len(data_rows), batch_size):
2151
- batch = data_rows[i:i + batch_size]
2152
-
2153
- # 创建批量操作
2154
- operations = []
2155
- for row_data in batch:
2156
- operations.append({
2157
- "create": {"position": i}
2158
- })
2159
-
2160
- # 执行批量创建
2161
- result = await client.taples.batch_edit_rows(
2162
- sheet_id=sheet_id,
2163
- operations=operations
2164
- )
2165
-
2166
- if not result.get('success'):
2167
- print(f"批次 {i//batch_size + 1} 失败: {result.get('error_message')}")
2168
- continue
2169
-
2170
- # 批量填充单元格数据
2171
- cell_operations = []
2172
- # ... 构建单元格操作
2173
-
2174
- await client.taples.batch_edit_cells(
2175
- sheet_id=sheet_id,
2176
- operations=cell_operations
2177
- )
2178
- ```
2179
-
2180
- ### 并发控制
2181
-
2182
- 使用异步客户端时,可以充分利用并发提高效率:
2183
-
2184
- ```python
2185
- import asyncio
2186
- from file_hub_client import AsyncTamarFileHubClient
2187
-
2188
- async def concurrent_uploads(file_paths):
2189
- async with AsyncTamarFileHubClient() as client:
2190
- client.set_user_context(org_id="123", user_id="456")
2191
-
2192
- # 并发上传多个文件
2193
- tasks = []
2194
- for file_path in file_paths:
2195
- task = client.blobs.upload(file_path)
2196
- tasks.append(task)
2197
-
2198
- # 等待所有上传完成
2199
- results = await asyncio.gather(*tasks, return_exceptions=True)
2200
-
2201
- # 处理结果
2202
- for i, result in enumerate(results):
2203
- if isinstance(result, Exception):
2204
- print(f"文件 {file_paths[i]} 上传失败: {result}")
2205
- else:
2206
- print(f"文件 {file_paths[i]} 上传成功: {result.file.id}")
2207
-
2208
- # 使用信号量限制并发数
2209
- async def controlled_concurrent_operations(items, max_concurrent=10):
2210
- semaphore = asyncio.Semaphore(max_concurrent)
2211
-
2212
- async def process_item(item):
2213
- async with semaphore:
2214
- # 处理单个项目
2215
- return await some_operation(item)
2216
-
2217
- tasks = [process_item(item) for item in items]
2218
- return await asyncio.gather(*tasks)
2219
- ```
2220
-
2221
- ### 流式处理大数据
2222
-
2223
- 对于大量数据的处理,使用流式API避免内存溢出:
2224
-
2225
- ```python
2226
- # 流式读取大型表格数据
2227
- async def stream_table_data(client, sheet_id):
2228
- page = 1
2229
- page_size = 1000
2230
-
2231
- while True:
2232
- # 分页获取数据
2233
- result = await client.taples.get_sheet_data(
2234
- sheet_id=sheet_id,
2235
- page=page,
2236
- page_size=page_size
2237
- )
2238
-
2239
- if not result.rows:
2240
- break
2241
-
2242
- # 处理当前页数据
2243
- for row in result.rows:
2244
- yield row
2245
-
2246
- page += 1
2247
-
2248
- # 使用示例
2249
- async def process_large_table():
2250
- async with AsyncTamarFileHubClient() as client:
2251
- async for row in stream_table_data(client, "sheet-123"):
2252
- # 处理每一行数据
2253
- process_row(row)
2254
- ```
2255
-
2256
- ## 开发
2257
-
2258
- ### 生成 gRPC 代码
2259
-
2260
- 当 proto 文件更新后,需要重新生成代码:
2261
-
2262
- ```bash
2263
- # 使用命令行工具
2264
- file-hub-gen-proto
2265
-
2266
- # 或直接运行脚本
2267
- cd file_hub_client/rpc
2268
- python generate_grpc.py
2269
- ```
2270
-
2271
- ### 运行测试
2272
-
2273
- ```bash
2274
- # 运行所有测试
2275
- python tests/taple/run_all_tests.py
2276
-
2277
- # 运行特定测试
2278
- python tests/taple/test_table_operations.py
2279
-
2280
- # 设置测试环境变量
2281
- export TEST_SERVER_HOST=your-test-server.com
2282
- export TEST_SERVER_PORT=50051
2283
- export TEST_ORG_ID=test-org-123
2284
- export TEST_USER_ID=test-user-456
2285
- ```
2286
-
2287
- ## 故障排除
2288
-
2289
- ### 常见问题
2290
-
2291
- 1. **连接超时**
2292
- ```python
2293
- # 增加超时时间
2294
- client = AsyncTamarFileHubClient(
2295
- retry_count=5,
2296
- retry_delay=2.0
2297
- )
2298
- ```
2299
-
2300
- 2. **版本冲突**
2301
- ```python
2302
- # 自动重试版本冲突
2303
- while True:
2304
- try:
2305
- result = await client.taples.create_column(...)
2306
- break
2307
- except VersionConflictError:
2308
- # 重新获取版本并重试
2309
- continue
2310
- ```
2311
-
2312
- 3. **大文件上传失败**
2313
- ```python
2314
- # 使用断点续传模式
2315
- file_info = await client.blobs.upload(
2316
- "large_file.zip",
2317
- mode=UploadMode.RESUMABLE
2318
- )
2319
- ```
2320
-
2321
- ### 调试技巧
2322
-
2323
- 1. **启用详细日志**
2324
- ```python
2325
- client = AsyncTamarFileHubClient(
2326
- enable_logging=True,
2327
- log_level="DEBUG"
2328
- )
2329
- ```
2330
-
2331
- 2. **使用请求ID追踪**
2332
- ```python
2333
- # 为每个操作设置唯一的请求ID
2334
- request_id = f"debug-{operation}-{timestamp}"
2335
- result = await client.taples.create_table(
2336
- name="test",
2337
- request_id=request_id
2338
- )
2339
- ```
2340
-
2341
- 3. **检查网络连接**
2342
- ```python
2343
- # 测试连接
2344
- try:
2345
- await client.connect()
2346
- print("连接成功")
2347
- except ConnectionError as e:
2348
- print(f"连接失败: {e}")
2349
- ```
2350
-
2351
- ## 最佳实践
2352
-
2353
- 1. **使用单例客户端**:避免频繁创建客户端实例
2354
- 2. **设置合理的超时和重试**:根据网络环境调整
2355
- 3. **使用幂等性键**:防止重复操作
2356
- 4. **批量操作**:提高性能
2357
- 5. **错误处理**:妥善处理各种异常
2358
- 6. **资源清理**:使用 with 语句确保资源释放
2359
- 7. **并发控制**:合理使用并发避免服务器过载
2360
- 8. **AI生成文件处理**:
2361
- - ✅ **推荐**: 上传AI生成的字节数据时显式提供 `mime_type` 参数
2362
- - ✅ **备选**: 依赖自动检测(支持26+种格式的magic bytes检测)
2363
- - ✅ **兼容**: 无需修改现有代码,保持100%向下兼容
2364
- - ⚠️ **注意**: 断点续传现已完全支持MIME类型传递
2365
-
2366
- ## 许可证
2367
-
2368
- MIT License
2369
-
2370
- ## 贡献
2371
-
2372
- 欢迎提交 Issue 和 Pull Request!
2373
-
2374
- ## 更新日志
2375
-
2376
- ### v0.0.7 (2025-09)
2377
- - **重大修复**: 修复MIME类型检测和文件扩展名推断功能
2378
- - **断点续传修复**: 解决断点续传中的HTTP头部和签名验证问题
2379
- - **AI生成文件支持**: 完善对AI生成内容(图片、视频、音频)的MIME类型处理
2380
- - **新功能**: 新增 `mime_type` 参数支持,允许用户显式指定文件MIME类型
2381
- - **魔术字节检测**: 增强内容检测,支持26+种主流文件格式的自动识别
2382
- - **向下兼容**: 保持100%向下兼容,现有代码无需修改
2383
- - **核心修复**:
2384
- - 修复 `upload_helper.py` 中系统性拼写错误(`mine_type` → `mime_type`)
2385
- - 修复断点续传缺失 `Cache-Control` 头部导致的400错误
2386
- - 修复AI生成文件默认使用 `.dat` 扩展名的问题
2387
- - 增强MIME类型到文件扩展名的映射(50+种MIME类型支持)
2388
- - **文件格式支持**: PNG, JPEG, WebP, MP4, MP3, WAV, GIF, BMP, PDF等主流格式
2389
- - **使用场景**: 完美支持AI模型输出的字节数据+MIME类型组合
2390
-
2391
- ### v0.0.6 (2025-08)
2392
- - 新增媒体文件压缩服务功能
2393
- - 支持获取文件压缩状态 (get_compression_status)
2394
- - 支持获取压缩变体列表 (get_compressed_variants)
2395
- - 支持触发文件重新压缩 (trigger_recompression)
2396
- - 支持生成变体下载URL (generate_variant_download_url)
2397
- - 添加压缩相关数据模型 (CompressedVariant 等)
2398
- - 在所有文件服务类中实现压缩功能支持
2399
- - 更新文档包含压缩服务使用示例
2400
-
2401
- ### v0.0.5 (2025-01)
2402
- - 新增批量下载URL生成接口 (batch_generate_download_url)
2403
- - 新增GCS URL获取接口 (get_gcs_url, batch_get_gcs_url)
2404
- - **重要更新**: 批量下载URL接口 (BatchGenerateDownloadUrl) 现在返回MIME类型信息
2405
- - **重要更新**: DownloadUrlInfo 结构新增 mime_type 字段,便于文件类型识别
2406
- - GCS URL接口返回MIME类型信息,便于文件类型识别
2407
- - 新增 keep_original_filename 参数支持保留原始文件名
2408
- - 更新相关文档和测试用例
2409
-
2410
- ### v0.0.4 (2025-01)
2411
- - 新增从URL上传文件功能
2412
- - 支持自动下载URL内容并上传到GCS
2413
- - 支持自定义文件名
2414
- - 修复URL上传时的MIME类型检测问题
2415
- - 改进测试中对哈希去重的说明
2416
-
2417
- ### v0.0.3 (2025-07)
2418
- - 端口参数变为可选,支持直接使用域名连接
2419
- - 优化环境变量端口配置处理
2420
- - 改进连接地址构建逻辑
2421
-
2422
- ### v1.5.0 (2025-01)
2423
- - 添加 gRPC 请求自动日志记录
2424
- - 支持 JSON 格式日志输出
2425
- - 日志消息中文化并添加图标
2426
- - 优化 CSV 文件 MIME 类型检测
2427
- - 修复拦截器类型错误问题
2428
-
2429
- ### v1.4.0 (2024-12)
2430
- - 添加 Taple 表格导入导出功能
2431
- - 支持表格克隆操作
2432
- - 优化批量操作性能
2433
- - 增强幂等性支持
2434
-
2435
- ### v1.3.0 (2024-11)
2436
- - 添加完整的 Taple 电子表格支持
2437
- - 实现乐观锁版本控制
2438
- - 支持合并单元格和视图管理
2439
-
2440
- ### v1.2.0 (2024-10)
2441
- - 重构服务架构,实现分层设计
2442
- - 添加请求ID追踪功能
2443
- - 增强用户上下文管理
2444
-
2445
- ### v1.1.0 (2024-09)
2446
- - 添加 TLS/SSL 支持
2447
- - 实现自动重试机制
2448
- - 优化大文件上传下载
2449
-
2450
- ### v1.0.0 (2024-08)
2451
- - 初始版本发布
2452
- - 基础文件和文件夹操作
2453
- - 异步和同步双客户端支持