tamar-file-hub-client 0.1.4__py3-none-any.whl → 0.1.6__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.
- file_hub_client/rpc/gen/file_service_pb2.py +30 -30
- file_hub_client/rpc/interceptors.py +578 -580
- file_hub_client/rpc/protos/file_service.proto +2 -1
- file_hub_client/schemas/file.py +171 -170
- file_hub_client/services/file/async_blob_service.py +25 -8
- file_hub_client/services/file/base_file_service.py +210 -9
- file_hub_client/services/file/sync_blob_service.py +25 -8
- file_hub_client/utils/__init__.py +10 -0
- file_hub_client/utils/logging.py +335 -318
- file_hub_client/utils/mime_extension_mapper.py +158 -0
- file_hub_client/utils/upload_helper.py +36 -22
- {tamar_file_hub_client-0.1.4.dist-info → tamar_file_hub_client-0.1.6.dist-info}/METADATA +67 -2
- {tamar_file_hub_client-0.1.4.dist-info → tamar_file_hub_client-0.1.6.dist-info}/RECORD +15 -14
- {tamar_file_hub_client-0.1.4.dist-info → tamar_file_hub_client-0.1.6.dist-info}/WHEEL +0 -0
- {tamar_file_hub_client-0.1.4.dist-info → tamar_file_hub_client-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
"""
|
2
|
+
MIME类型到文件扩展名的映射工具
|
3
|
+
用于从MIME类型推断正确的文件扩展名,特别适用于AI模型生成的文件
|
4
|
+
"""
|
5
|
+
from typing import Optional, Dict
|
6
|
+
|
7
|
+
|
8
|
+
class MimeExtensionMapper:
|
9
|
+
"""MIME类型到文件扩展名的映射器"""
|
10
|
+
|
11
|
+
# MIME类型到文件扩展名的映射表
|
12
|
+
MIME_TO_EXTENSION: Dict[str, str] = {
|
13
|
+
# 图片类型(AI生图常用)
|
14
|
+
'image/jpeg': 'jpg',
|
15
|
+
'image/jpg': 'jpg',
|
16
|
+
'image/png': 'png',
|
17
|
+
'image/gif': 'gif',
|
18
|
+
'image/webp': 'webp',
|
19
|
+
'image/bmp': 'bmp',
|
20
|
+
'image/tiff': 'tiff',
|
21
|
+
'image/svg+xml': 'svg',
|
22
|
+
'image/x-icon': 'ico',
|
23
|
+
|
24
|
+
# 视频类型(AI生视频常用)
|
25
|
+
'video/mp4': 'mp4',
|
26
|
+
'video/mpeg': 'mpeg',
|
27
|
+
'video/quicktime': 'mov',
|
28
|
+
'video/x-msvideo': 'avi',
|
29
|
+
'video/webm': 'webm',
|
30
|
+
'video/x-flv': 'flv',
|
31
|
+
'video/3gpp': '3gp',
|
32
|
+
|
33
|
+
# 音频类型(AI生音频常用)
|
34
|
+
'audio/mpeg': 'mp3',
|
35
|
+
'audio/wav': 'wav',
|
36
|
+
'audio/x-wav': 'wav',
|
37
|
+
'audio/ogg': 'ogg',
|
38
|
+
'audio/mp4': 'm4a',
|
39
|
+
'audio/aac': 'aac',
|
40
|
+
'audio/flac': 'flac',
|
41
|
+
'audio/x-ms-wma': 'wma',
|
42
|
+
'audio/amr': 'amr',
|
43
|
+
'audio/basic': 'au',
|
44
|
+
'audio/aiff': 'aiff',
|
45
|
+
|
46
|
+
# 文档类型
|
47
|
+
'application/pdf': 'pdf',
|
48
|
+
'application/msword': 'doc',
|
49
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
50
|
+
'application/vnd.ms-excel': 'xls',
|
51
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
52
|
+
'application/vnd.ms-powerpoint': 'ppt',
|
53
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
54
|
+
'application/vnd.ms-office': 'doc', # MS Office通用格式
|
55
|
+
'application/vnd.openxmlformats-officedocument': 'docx', # Office 2007+通用格式
|
56
|
+
|
57
|
+
# 文本类型
|
58
|
+
'text/plain': 'txt',
|
59
|
+
'text/html': 'html',
|
60
|
+
'text/css': 'css',
|
61
|
+
'text/javascript': 'js',
|
62
|
+
'text/csv': 'csv',
|
63
|
+
'application/json': 'json',
|
64
|
+
'application/xml': 'xml',
|
65
|
+
'text/xml': 'xml',
|
66
|
+
|
67
|
+
# 压缩文件类型
|
68
|
+
'application/zip': 'zip',
|
69
|
+
'application/x-rar-compressed': 'rar',
|
70
|
+
'application/vnd.rar': 'rar',
|
71
|
+
'application/x-7z-compressed': '7z',
|
72
|
+
'application/x-tar': 'tar',
|
73
|
+
'application/gzip': 'gz',
|
74
|
+
'application/x-bzip2': 'bz2',
|
75
|
+
|
76
|
+
# 可执行文件类型
|
77
|
+
'application/x-msdownload': 'exe',
|
78
|
+
'application/x-executable': 'bin',
|
79
|
+
'application/x-mach-binary': 'bin',
|
80
|
+
|
81
|
+
# 其他常用类型
|
82
|
+
'application/octet-stream': 'dat', # 通用二进制文件,保持与现有逻辑一致
|
83
|
+
}
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
def get_extension_from_mime(cls, mime_type: str) -> Optional[str]:
|
87
|
+
"""
|
88
|
+
从MIME类型获取文件扩展名
|
89
|
+
|
90
|
+
Args:
|
91
|
+
mime_type: MIME类型字符串
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
文件扩展名(不包含点号),如果无法映射则返回None
|
95
|
+
"""
|
96
|
+
if not mime_type:
|
97
|
+
return None
|
98
|
+
|
99
|
+
# 清理MIME类型(去除参数部分,如charset等)
|
100
|
+
mime_type = mime_type.split(';')[0].strip().lower()
|
101
|
+
|
102
|
+
return cls.MIME_TO_EXTENSION.get(mime_type)
|
103
|
+
|
104
|
+
@classmethod
|
105
|
+
def get_extension_with_fallback(cls, mime_type: str, fallback: str = 'dat') -> str:
|
106
|
+
"""
|
107
|
+
从MIME类型获取文件扩展名,如果无法映射则返回fallback
|
108
|
+
|
109
|
+
Args:
|
110
|
+
mime_type: MIME类型字符串
|
111
|
+
fallback: 默认扩展名,当无法从MIME类型推断时使用
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
文件扩展名(不包含点号)
|
115
|
+
"""
|
116
|
+
extension = cls.get_extension_from_mime(mime_type)
|
117
|
+
return extension if extension is not None else fallback
|
118
|
+
|
119
|
+
@classmethod
|
120
|
+
def is_supported_mime(cls, mime_type: str) -> bool:
|
121
|
+
"""
|
122
|
+
检查是否支持该MIME类型的映射
|
123
|
+
|
124
|
+
Args:
|
125
|
+
mime_type: MIME类型字符串
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
是否支持该MIME类型
|
129
|
+
"""
|
130
|
+
return cls.get_extension_from_mime(mime_type) is not None
|
131
|
+
|
132
|
+
|
133
|
+
# 便捷函数
|
134
|
+
def get_extension_from_mime_type(mime_type: str) -> Optional[str]:
|
135
|
+
"""
|
136
|
+
从MIME类型获取文件扩展名的便捷函数
|
137
|
+
|
138
|
+
Args:
|
139
|
+
mime_type: MIME类型字符串
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
文件扩展名(不包含点号),如果无法映射则返回None
|
143
|
+
"""
|
144
|
+
return MimeExtensionMapper.get_extension_from_mime(mime_type)
|
145
|
+
|
146
|
+
|
147
|
+
def get_extension_from_mime_type_with_fallback(mime_type: str, fallback: str = 'dat') -> str:
|
148
|
+
"""
|
149
|
+
从MIME类型获取文件扩展名的便捷函数,带fallback
|
150
|
+
|
151
|
+
Args:
|
152
|
+
mime_type: MIME类型字符串
|
153
|
+
fallback: 默认扩展名
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
文件扩展名(不包含点号)
|
157
|
+
"""
|
158
|
+
return MimeExtensionMapper.get_extension_with_fallback(mime_type, fallback)
|
@@ -37,21 +37,25 @@ class HttpUploader:
|
|
37
37
|
self.retry_delay_seconds = retry_delay_seconds
|
38
38
|
|
39
39
|
def start_resumable_session(self, url: str, total_file_size: Optional[int] = None,
|
40
|
-
|
40
|
+
mime_type: Optional[str] = None) -> str:
|
41
41
|
"""
|
42
42
|
启动 GCS 的断点续传会话,返回 session URI。
|
43
43
|
|
44
44
|
Args:
|
45
45
|
url (str): GCS 预签名上传初始化 URL。
|
46
46
|
total_file_size (Optional[int]): 文件总大小(可选)。
|
47
|
-
|
47
|
+
mime_type (Optional[str]): 文件 Content-Type。
|
48
48
|
Returns:
|
49
49
|
str: GCS 返回的会话 URI。
|
50
50
|
"""
|
51
51
|
content_range_header = f"bytes */{total_file_size}" if total_file_size is not None else "bytes */*"
|
52
|
-
headers = {
|
53
|
-
|
54
|
-
|
52
|
+
headers = {
|
53
|
+
"Content-Range": content_range_header,
|
54
|
+
"x-goog-resumable": "start",
|
55
|
+
"Cache-Control": "public, max-age=86400" # 添加缺失的 cache-control 头部
|
56
|
+
}
|
57
|
+
if mime_type is not None:
|
58
|
+
headers["Content-Type"] = mime_type
|
55
59
|
|
56
60
|
response = self._request("POST", url, headers=headers)
|
57
61
|
if response.status_code in [200, 201]:
|
@@ -63,7 +67,7 @@ class HttpUploader:
|
|
63
67
|
raise Exception(f"Failed to start resumable session: {response.status_code} - {response.text}")
|
64
68
|
|
65
69
|
def check_uploaded_size(self, url: str, total_file_size: Optional[int] = None,
|
66
|
-
|
70
|
+
mime_type: Optional[str] = None) -> int:
|
67
71
|
"""
|
68
72
|
查询 GCS 可恢复上传的当前进度(已上传的字节数)。
|
69
73
|
|
@@ -72,7 +76,7 @@ class HttpUploader:
|
|
72
76
|
total_file_size (Optional[int]): 文件的总大小(可选)。
|
73
77
|
如果已知,提供此参数可以帮助 GCS 进行更精确的判断。
|
74
78
|
如果 GCS 响应 200 OK,且提供了此参数,则直接返回此值。
|
75
|
-
|
79
|
+
mime_type (Optional[str]): 文件 Content-Type。
|
76
80
|
Returns:
|
77
81
|
int: 已上传的字节数。
|
78
82
|
- 如果上传已完成 (200 OK),返回 total_file_size。如果 total_file_size 未知,则返回 0(表示需要服务器端后续验证)。
|
@@ -81,9 +85,12 @@ class HttpUploader:
|
|
81
85
|
"""
|
82
86
|
# 构建 Content-Range 头。如果知道总大小,提供它更准确。
|
83
87
|
content_range_header = f"bytes */{total_file_size}" if total_file_size is not None else "bytes */*"
|
84
|
-
headers = {
|
85
|
-
|
86
|
-
|
88
|
+
headers = {
|
89
|
+
"Content-Range": content_range_header,
|
90
|
+
"Cache-Control": "public, max-age=86400" # 添加缺失的 cache-control 头部
|
91
|
+
}
|
92
|
+
if mime_type is not None:
|
93
|
+
headers["Content-Type"] = mime_type
|
87
94
|
|
88
95
|
# 执行查询
|
89
96
|
response = self._request("PUT", url, headers=headers)
|
@@ -139,7 +146,7 @@ class HttpUploader:
|
|
139
146
|
|
140
147
|
# 若断点续传,查询 resume_from 位置
|
141
148
|
if is_resume:
|
142
|
-
resume_from = self.check_uploaded_size(url, final_total_size,
|
149
|
+
resume_from = self.check_uploaded_size(url, final_total_size, mime_type=headers.get("Content-Type"))
|
143
150
|
else:
|
144
151
|
resume_from = 0
|
145
152
|
|
@@ -258,21 +265,25 @@ class AsyncHttpUploader:
|
|
258
265
|
self.retry_delay_seconds = retry_delay_seconds
|
259
266
|
|
260
267
|
async def start_resumable_session(self, url: str, total_file_size: Optional[int] = None,
|
261
|
-
|
268
|
+
mime_type: Optional[str] = None) -> str:
|
262
269
|
"""
|
263
270
|
启动 GCS 的断点续传会话,返回 session URI。
|
264
271
|
|
265
272
|
Args:
|
266
273
|
url (str): GCS 预签名上传初始化 URL。
|
267
274
|
total_file_size (Optional[int]): 文件总大小(可选)。
|
268
|
-
|
275
|
+
mime_type (Optional[str]): 文件 Content-Type。
|
269
276
|
Returns:
|
270
277
|
str: GCS 返回的会话 URI。
|
271
278
|
"""
|
272
279
|
content_range_header = f"bytes */{total_file_size}" if total_file_size is not None else "bytes */*"
|
273
|
-
headers = {
|
274
|
-
|
275
|
-
|
280
|
+
headers = {
|
281
|
+
"Content-Range": content_range_header,
|
282
|
+
"x-goog-resumable": "start",
|
283
|
+
"Cache-Control": "public, max-age=86400" # 添加缺失的 cache-control 头部
|
284
|
+
}
|
285
|
+
if mime_type is not None:
|
286
|
+
headers["Content-Type"] = mime_type
|
276
287
|
|
277
288
|
response = await self._request("POST", url, headers=headers)
|
278
289
|
if response.status in [200, 201]:
|
@@ -285,7 +296,7 @@ class AsyncHttpUploader:
|
|
285
296
|
raise Exception(f"Failed to start resumable session: {response.status} - {text}")
|
286
297
|
|
287
298
|
async def check_uploaded_size(self, url: str, total_file_size: Optional[int] = None,
|
288
|
-
|
299
|
+
mime_type: Optional[str] = None) -> int:
|
289
300
|
"""
|
290
301
|
查询 GCS 可恢复上传的当前进度(已上传的字节数)。
|
291
302
|
|
@@ -294,7 +305,7 @@ class AsyncHttpUploader:
|
|
294
305
|
total_file_size (Optional[int]): 文件的总大小(可选)。
|
295
306
|
如果已知,提供此参数可以帮助 GCS 进行更精确的判断。
|
296
307
|
如果 GCS 响应 200 OK,且提供了此参数,则直接返回此值。
|
297
|
-
|
308
|
+
mime_type (Optional[str]): 文件 Content-Type。
|
298
309
|
Returns:
|
299
310
|
int: 已上传的字节数。
|
300
311
|
- 如果上传已完成 (200 OK),返回 total_file_size。如果 total_file_size 未知,则返回 0(表示需要服务器端后续验证)。
|
@@ -303,9 +314,12 @@ class AsyncHttpUploader:
|
|
303
314
|
"""
|
304
315
|
# 构建 Content-Range 头。如果知道总大小,提供它更准确。
|
305
316
|
content_range_header = f"bytes */{total_file_size}" if total_file_size is not None else "bytes */*"
|
306
|
-
headers = {
|
307
|
-
|
308
|
-
|
317
|
+
headers = {
|
318
|
+
"Content-Range": content_range_header,
|
319
|
+
"Cache-Control": "public, max-age=86400" # 添加缺失的 cache-control 头部
|
320
|
+
}
|
321
|
+
if mime_type is not None:
|
322
|
+
headers["Content-Type"] = mime_type
|
309
323
|
|
310
324
|
# 发送一个空的 PUT 请求来查询已上传字节数
|
311
325
|
# timeout 应该根据网络情况和预期响应时间设置
|
@@ -364,7 +378,7 @@ class AsyncHttpUploader:
|
|
364
378
|
|
365
379
|
# 如果是断点续传,查询服务端 resume_from
|
366
380
|
if is_resume:
|
367
|
-
resume_from = await self.check_uploaded_size(url, final_total_size,
|
381
|
+
resume_from = await self.check_uploaded_size(url, final_total_size, mime_type=headers.get("Content-Type"))
|
368
382
|
else:
|
369
383
|
resume_from = 0
|
370
384
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: tamar-file-hub-client
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.6
|
4
4
|
Summary: A Python SDK for gRPC-based file management system
|
5
5
|
Home-page: https://github.com/Tamar-Edge-AI/file-hub-client
|
6
6
|
Author: Oscar Ou
|
@@ -55,6 +55,8 @@ Dynamic: summary
|
|
55
55
|
- 📂 **文件夹管理**:支持文件夹的创建、重命名、移动、删除
|
56
56
|
- 🔗 **文件分享**:支持生成分享链接,设置访问权限和密码
|
57
57
|
- 🔄 **多种上传方式**:支持直传、断点续传、客户端直传到对象存储
|
58
|
+
- 🎯 **智能MIME类型检测**:支持26+种主流文件格式的魔术字节检测和扩展名推断
|
59
|
+
- 🤖 **AI生成文件支持**:完美支持AI模型输出的字节数据+MIME类型组合上传
|
58
60
|
- 🛡️ **错误处理**:完善的异常体系和错误重试机制
|
59
61
|
- 🔒 **TLS/SSL 支持**:支持安全的加密连接,保护数据传输
|
60
62
|
- 🔁 **自动重试**:连接失败时自动重试,提高可靠性
|
@@ -615,6 +617,34 @@ async with AsyncTamarFileHubClient() as client:
|
|
615
617
|
file_info = await client.blobs.upload(f)
|
616
618
|
```
|
617
619
|
|
620
|
+
#### AI生成文件上传(新功能)
|
621
|
+
|
622
|
+
```python
|
623
|
+
from file_hub_client import AsyncTamarFileHubClient
|
624
|
+
|
625
|
+
async with AsyncTamarFileHubClient() as client:
|
626
|
+
# AI模型返回的字节数据(图片、音频、视频等)
|
627
|
+
# 场景:AI生图模型返回WebP格式图片
|
628
|
+
ai_image_data = b"\x52\x49\x46\x46...." # WebP格式的字节数据
|
629
|
+
|
630
|
+
# 方式1:显式指定MIME类型(推荐用于AI生成内容)
|
631
|
+
file_info = await client.blobs.upload(
|
632
|
+
file=ai_image_data,
|
633
|
+
mime_type="image/webp" # 明确指定MIME类型
|
634
|
+
)
|
635
|
+
print(f"AI生成图片上传成功: {file_info.file.file_name}") # upload_xxx.webp
|
636
|
+
|
637
|
+
# 方式2:自动检测MIME类型(支持26+种格式)
|
638
|
+
file_info = await client.blobs.upload(file=ai_image_data)
|
639
|
+
# 系统会自动检测magic bytes并推断为WebP格式
|
640
|
+
|
641
|
+
# 支持的AI生成内容格式:
|
642
|
+
# 🖼️ 图片: PNG, JPEG, WebP, GIF, BMP等
|
643
|
+
# 🎵 音频: MP3, WAV, FLAC, AAC, OGG等
|
644
|
+
# 🎥 视频: MP4, MOV, WebM, AVI等
|
645
|
+
# 📄 文档: PDF, TXT等
|
646
|
+
```
|
647
|
+
|
618
648
|
#### 大文件上传(流式上传和断点续传)
|
619
649
|
|
620
650
|
```python
|
@@ -743,7 +773,14 @@ async with AsyncTamarFileHubClient() as client:
|
|
743
773
|
if url_info.error:
|
744
774
|
print(f"文件 {url_info.file_id} 生成URL失败: {url_info.error}")
|
745
775
|
else:
|
746
|
-
print(f"文件 {url_info.file_id}
|
776
|
+
print(f"文件 {url_info.file_id}:")
|
777
|
+
print(f" 下载URL: {url_info.url}")
|
778
|
+
print(f" MIME类型: {url_info.mime_type}")
|
779
|
+
# 根据MIME类型处理文件
|
780
|
+
if url_info.mime_type.startswith('image/'):
|
781
|
+
print(f" 这是一个图片文件")
|
782
|
+
elif url_info.mime_type == 'application/pdf':
|
783
|
+
print(f" 这是一个PDF文件")
|
747
784
|
```
|
748
785
|
|
749
786
|
同步客户端示例:
|
@@ -760,6 +797,12 @@ with TamarFileHubClient() as client:
|
|
760
797
|
)
|
761
798
|
```
|
762
799
|
|
800
|
+
**注意**:`batch_generate_download_url` 方法返回一个 `BatchDownloadUrlResponse` 对象,其中 `download_urls` 字段包含 `DownloadUrlInfo` 对象列表,每个对象包含:
|
801
|
+
- `file_id`: 文件ID
|
802
|
+
- `url`: 下载URL(如果成功生成)
|
803
|
+
- `mime_type`: 文件的MIME类型,便于正确处理文件内容
|
804
|
+
- `error`: 错误信息(如果生成失败)
|
805
|
+
|
763
806
|
##### 获取GCS URL
|
764
807
|
|
765
808
|
对于需要直接访问Google Cloud Storage的场景,可以获取文件的GCS URL和MIME类型信息:
|
@@ -2314,6 +2357,11 @@ export TEST_USER_ID=test-user-456
|
|
2314
2357
|
5. **错误处理**:妥善处理各种异常
|
2315
2358
|
6. **资源清理**:使用 with 语句确保资源释放
|
2316
2359
|
7. **并发控制**:合理使用并发避免服务器过载
|
2360
|
+
8. **AI生成文件处理**:
|
2361
|
+
- ✅ **推荐**: 上传AI生成的字节数据时显式提供 `mime_type` 参数
|
2362
|
+
- ✅ **备选**: 依赖自动检测(支持26+种格式的magic bytes检测)
|
2363
|
+
- ✅ **兼容**: 无需修改现有代码,保持100%向下兼容
|
2364
|
+
- ⚠️ **注意**: 断点续传现已完全支持MIME类型传递
|
2317
2365
|
|
2318
2366
|
## 许可证
|
2319
2367
|
|
@@ -2325,6 +2373,21 @@ MIT License
|
|
2325
2373
|
|
2326
2374
|
## 更新日志
|
2327
2375
|
|
2376
|
+
### v0.0.7 (2025-09)
|
2377
|
+
- **重大修复**: 修复MIME类型检测和文件扩展名推断功能
|
2378
|
+
- **断点续传修复**: 解决断点续传中的HTTP头部和签名验证问题
|
2379
|
+
- **AI生成文件支持**: 完善对AI生成内容(图片、视频、音频)的MIME类型处理
|
2380
|
+
- **新功能**: 新增 `mime_type` 参数支持,允许用户显式指定文件MIME类型
|
2381
|
+
- **魔术字节检测**: 增强内容检测,支持26+种主流文件格式的自动识别
|
2382
|
+
- **向下兼容**: 保持100%向下兼容,现有代码无需修改
|
2383
|
+
- **核心修复**:
|
2384
|
+
- 修复 `upload_helper.py` 中系统性拼写错误(`mine_type` → `mime_type`)
|
2385
|
+
- 修复断点续传缺失 `Cache-Control` 头部导致的400错误
|
2386
|
+
- 修复AI生成文件默认使用 `.dat` 扩展名的问题
|
2387
|
+
- 增强MIME类型到文件扩展名的映射(50+种MIME类型支持)
|
2388
|
+
- **文件格式支持**: PNG, JPEG, WebP, MP4, MP3, WAV, GIF, BMP, PDF等主流格式
|
2389
|
+
- **使用场景**: 完美支持AI模型输出的字节数据+MIME类型组合
|
2390
|
+
|
2328
2391
|
### v0.0.6 (2025-08)
|
2329
2392
|
- 新增媒体文件压缩服务功能
|
2330
2393
|
- 支持获取文件压缩状态 (get_compression_status)
|
@@ -2338,6 +2401,8 @@ MIT License
|
|
2338
2401
|
### v0.0.5 (2025-01)
|
2339
2402
|
- 新增批量下载URL生成接口 (batch_generate_download_url)
|
2340
2403
|
- 新增GCS URL获取接口 (get_gcs_url, batch_get_gcs_url)
|
2404
|
+
- **重要更新**: 批量下载URL接口 (BatchGenerateDownloadUrl) 现在返回MIME类型信息
|
2405
|
+
- **重要更新**: DownloadUrlInfo 结构新增 mime_type 字段,便于文件类型识别
|
2341
2406
|
- GCS URL接口返回MIME类型信息,便于文件类型识别
|
2342
2407
|
- 新增 keep_original_filename 参数支持保留原始文件名
|
2343
2408
|
- 更新相关文档和测试用例
|
@@ -10,29 +10,29 @@ file_hub_client/errors/exceptions.py,sha256=0RDkAv4t2GeVefm1H5eaXGxd6Od-drXzPIVJ
|
|
10
10
|
file_hub_client/rpc/__init__.py,sha256=dhRt0D_US3j2Dam47nhd23RowVZ-nvVowhPsNFf4GZo,204
|
11
11
|
file_hub_client/rpc/async_client.py,sha256=qWrqRA3UhsKgCjM5G-jncnN2Xgu8-XqMFI2i5nBRSSs,15348
|
12
12
|
file_hub_client/rpc/generate_grpc.py,sha256=opzstxWdW7vqR9OxrgUCSUkZe8IqgcOdruqWGIzCneI,2158
|
13
|
-
file_hub_client/rpc/interceptors.py,sha256=
|
13
|
+
file_hub_client/rpc/interceptors.py,sha256=cFLKziOZMO5oKZfElLXctLKo08BJCFHFiuCrvtYSGaw,21627
|
14
14
|
file_hub_client/rpc/sync_client.py,sha256=iaow1Lpi0moq9kwlUVNP0p4ucgHKCP8y5cvmP32dkQU,15364
|
15
15
|
file_hub_client/rpc/gen/__init__.py,sha256=NJLqr9ezUXeOyy1J0sMPn3Kl_8IyGw7GAzzzewO3MIw,45
|
16
|
-
file_hub_client/rpc/gen/file_service_pb2.py,sha256=
|
16
|
+
file_hub_client/rpc/gen/file_service_pb2.py,sha256=WQzve9AfDRJy0xJ-hIUX3A0xyL5h9fpyRVemLkP4SPA,14807
|
17
17
|
file_hub_client/rpc/gen/file_service_pb2_grpc.py,sha256=a5tjnCqO_ZVBBQK5COUgZ_Eb7hlv81yseg-8goOROls,33372
|
18
18
|
file_hub_client/rpc/gen/folder_service_pb2.py,sha256=OPNYHThmL1k2KH1ll2L_Gj76oWBWqcymXKHu8mk1Zvk,4126
|
19
19
|
file_hub_client/rpc/gen/folder_service_pb2_grpc.py,sha256=pOHFsVThdAu5DAKd8QK3_zQvUmFgGlh4KBqIYnYDZ_U,10411
|
20
20
|
file_hub_client/rpc/gen/taple_service_pb2.py,sha256=8BJCFRFJ_khl_Uaw-5jWCabljWJo6zaqOYWe1368xEw,48254
|
21
21
|
file_hub_client/rpc/gen/taple_service_pb2_grpc.py,sha256=DCL-45PlEZ0guLz1u2fFH_sDIT_awuSvp9Ak6fa2GO8,64773
|
22
|
-
file_hub_client/rpc/protos/file_service.proto,sha256=
|
22
|
+
file_hub_client/rpc/protos/file_service.proto,sha256=KuayH0TNEJENI77LG6VUyUHPpFZuKJ1I5ljvoJyRIHo,6924
|
23
23
|
file_hub_client/rpc/protos/folder_service.proto,sha256=cgIbJT2slXMMRGrtrzN3kjae9-4CB1zXCmceiUgE6fI,1542
|
24
24
|
file_hub_client/rpc/protos/taple_service.proto,sha256=0mwhyBwD3yvFhMiiSA6J3Ni6pyHc369iD4oDXXl7DIU,29765
|
25
25
|
file_hub_client/schemas/__init__.py,sha256=oaXrODfBI-Oe-MyAyL6bKDZ1mPRC_IyMmxzGic4BbEU,2692
|
26
26
|
file_hub_client/schemas/context.py,sha256=E4TKqUJBaq9s31cGIRsztS-mQBUYAKyyLK8knVgWNb8,5805
|
27
|
-
file_hub_client/schemas/file.py,sha256=
|
27
|
+
file_hub_client/schemas/file.py,sha256=KaF7XaZLkIlomEPaC1G9Qsbzen6VfChwVrXFRydWowA,6709
|
28
28
|
file_hub_client/schemas/folder.py,sha256=D7UFsLCou-7CCXCQvuRObaBQEGmETsm1cgGOG1ceSrk,1026
|
29
29
|
file_hub_client/schemas/taple.py,sha256=LYsECsDbcioPXcvjRBcCEbh083iEB-eFCapJrGMJ8w0,17790
|
30
30
|
file_hub_client/services/__init__.py,sha256=yh5mir0dKB_LtJMk2hTpQI9WSlguaxtVD2KomMnzxdM,514
|
31
31
|
file_hub_client/services/file/__init__.py,sha256=aJygo_AzYk5NN-ezp-a9YlugJ82wVIP9e5e54fl0UsI,342
|
32
|
-
file_hub_client/services/file/async_blob_service.py,sha256=
|
32
|
+
file_hub_client/services/file/async_blob_service.py,sha256=J8u1DCaeGgqGHB6ss7QB3nDypFHUQEIlHB2rqkS1BHk,36461
|
33
33
|
file_hub_client/services/file/async_file_service.py,sha256=lFMfnHKsbTIOpMAdFvER4B2JqVGz9SNzgxlmA1kY3Js,17197
|
34
|
-
file_hub_client/services/file/base_file_service.py,sha256=
|
35
|
-
file_hub_client/services/file/sync_blob_service.py,sha256=
|
34
|
+
file_hub_client/services/file/base_file_service.py,sha256=21C-z8zU2ooZp31YBTOAzvUJKbC5zUQ7T0pSmFA8iRs,16144
|
35
|
+
file_hub_client/services/file/sync_blob_service.py,sha256=HArzV6EhNf1N5Fnl_BNSw_UU3tiha0nIT-qy1GMuIBw,36108
|
36
36
|
file_hub_client/services/file/sync_file_service.py,sha256=3VEgFiWYfoWD4iZObJ8uylUtNmK08snXWUaVEosUPDo,16979
|
37
37
|
file_hub_client/services/folder/__init__.py,sha256=vGbMOlNiEBdnWZB1xE74RJtoroI28hKHCWfQV1GqKQc,210
|
38
38
|
file_hub_client/services/folder/async_folder_service.py,sha256=uFEmtW8EXYvaKYT2JCitWbdTGR1EtHlx_eBN5P3JUZg,7293
|
@@ -42,17 +42,18 @@ file_hub_client/services/taple/async_taple_service.py,sha256=XZqMBN69FSJ8-n0nyIf
|
|
42
42
|
file_hub_client/services/taple/base_taple_service.py,sha256=m_RZjvlXD8CzsB4gtZWDKiL7TNYYtRuSeY4yrFDnGIs,15842
|
43
43
|
file_hub_client/services/taple/idempotent_taple_mixin.py,sha256=lZeMF59dU-KVnc2p7epGrclidCv0nYg8TP_qVUezJ48,4295
|
44
44
|
file_hub_client/services/taple/sync_taple_service.py,sha256=LIfpJjZjUPpee5zidrmiRD-tTXPouSlU4RpScy5kNZY,87392
|
45
|
-
file_hub_client/utils/__init__.py,sha256=
|
45
|
+
file_hub_client/utils/__init__.py,sha256=KsgwKa_VQgFCsxx1ZvN4MWNp3qSmFbVyeC2YewkPtzg,2458
|
46
46
|
file_hub_client/utils/converter.py,sha256=TX69Bqk-PwNdv2hYQ07_tW6HQnQycHcJkGeRnskeF3A,3734
|
47
47
|
file_hub_client/utils/download_helper.py,sha256=Mc8TQSWjHxIglJMkKlGy9r3LZe8e_Mwe6D3sfn6IOnY,13338
|
48
48
|
file_hub_client/utils/file_utils.py,sha256=x4Oky_ZpmZ3TuLB7SjmCuzYiSqOyCuAMLe_2A6OjdFg,5537
|
49
49
|
file_hub_client/utils/idempotency.py,sha256=zuXDlpAc9VTkTsarlnkO0VuJ77yON6j1TX0GvL9Xd9k,6029
|
50
50
|
file_hub_client/utils/ip_detector.py,sha256=S1pHn5CSdLLOrIeIxU_qI7zA7bRW6pUOXVIbRuGHunc,6184
|
51
|
-
file_hub_client/utils/logging.py,sha256=
|
51
|
+
file_hub_client/utils/logging.py,sha256=CZi-YUxQS5ss79pTdqMO_QFZCU3PxGEB6LKoJu21a0Q,12457
|
52
|
+
file_hub_client/utils/mime_extension_mapper.py,sha256=jZhgKJcp-xFVDtaFBgUYaGcY4-4anBBABI19pacJ1Ms,5275
|
52
53
|
file_hub_client/utils/retry.py,sha256=A2MBdJCEY-Ks0guq8dd5wXX22sD27N30Qy3nQIW1B_s,18019
|
53
54
|
file_hub_client/utils/smart_retry.py,sha256=RjBhyG6SNDfMXxNxKU_qayWDD6Ihp7ow6_BPjhgflM0,16465
|
54
|
-
file_hub_client/utils/upload_helper.py,sha256=
|
55
|
-
tamar_file_hub_client-0.1.
|
56
|
-
tamar_file_hub_client-0.1.
|
57
|
-
tamar_file_hub_client-0.1.
|
58
|
-
tamar_file_hub_client-0.1.
|
55
|
+
file_hub_client/utils/upload_helper.py,sha256=mOnb_FGn-JLS-1uiC_LvDMOH0Y9-xvVo9QAJxid-GvI,23071
|
56
|
+
tamar_file_hub_client-0.1.6.dist-info/METADATA,sha256=QaSIumQwrxDpcocngxhwVZNA48J09RoJ8CLF_nqebPk,80363
|
57
|
+
tamar_file_hub_client-0.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
58
|
+
tamar_file_hub_client-0.1.6.dist-info/top_level.txt,sha256=9wcR7hyAJQdJg_kuH6WR3nmpJ8O-j8aJNK8f_kcFy6U,16
|
59
|
+
tamar_file_hub_client-0.1.6.dist-info/RECORD,,
|
File without changes
|
{tamar_file_hub_client-0.1.4.dist-info → tamar_file_hub_client-0.1.6.dist-info}/top_level.txt
RENAMED
File without changes
|