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.
- file_hub_client/rpc/gen/file_service_pb2.py +82 -82
- file_hub_client/rpc/protos/file_service.proto +1 -0
- file_hub_client/services/file/async_blob_service.py +27 -3
- file_hub_client/services/file/sync_blob_service.py +28 -4
- file_hub_client/utils/logging.py +11 -15
- file_hub_client/utils/upload_helper.py +67 -3
- tamar_file_hub_client-0.2.3.dist-info/METADATA +704 -0
- {tamar_file_hub_client-0.2.1.dist-info → tamar_file_hub_client-0.2.3.dist-info}/RECORD +10 -10
- tamar_file_hub_client-0.2.1.dist-info/METADATA +0 -2640
- {tamar_file_hub_client-0.2.1.dist-info → tamar_file_hub_client-0.2.3.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.2.1.dist-info → tamar_file_hub_client-0.2.3.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
- 异步和同步双客户端支持
|