tamar-file-hub-client 0.2.1__py3-none-any.whl → 0.2.3__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.

Potentially problematic release.


This version of tamar-file-hub-client might be problematic. Click here for more details.

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