fiuai-s3 0.4.0__tar.gz → 0.4.2__tar.gz
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 fiuai-s3 might be problematic. Click here for more details.
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/PKG-INFO +1 -1
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/pyproject.toml +1 -1
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/__init__.py +1 -1
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/alicloud/alicloud_storage.py +104 -1
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/minio/minio_storage.py +108 -9
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/object_storage.py +108 -2
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/.gitignore +0 -0
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/LICENSE +0 -0
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/README.md +0 -0
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/alicloud/__init__.py +0 -0
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/minio/__init__.py +0 -0
- {fiuai_s3-0.4.0 → fiuai_s3-0.4.2}/src/fiuai_s3/type.py +0 -0
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
from .object_storage import ObjectStorage, ObjectStorageFactory, StorageConfig
|
|
9
9
|
from .type import DocFileObject, DocSourceFrom, DocFileType
|
|
10
10
|
|
|
11
|
-
__version__ = "0.
|
|
11
|
+
__version__ = "0.4.1"
|
|
12
12
|
__all__ = ["ObjectStorage", "ObjectStorageFactory", "StorageConfig", "DocFileObject", "DocSourceFrom", "DocFileType"]
|
|
@@ -149,4 +149,107 @@ class AliCloudStorage(ObjectStorage):
|
|
|
149
149
|
return files
|
|
150
150
|
except Exception as e:
|
|
151
151
|
logger.error(f"列出单据文件失败: {str(e)}")
|
|
152
|
-
return []
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
|
|
155
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
156
|
+
auth_tenant_id: Optional[str] = None,
|
|
157
|
+
auth_company_id: Optional[str] = None,
|
|
158
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
159
|
+
"""
|
|
160
|
+
生成预签名URL
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
object_key: 对象存储中的key
|
|
164
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
165
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
166
|
+
response_headers: 响应头设置
|
|
167
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
168
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
169
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
173
|
+
"""
|
|
174
|
+
try:
|
|
175
|
+
# 验证参数
|
|
176
|
+
if not object_key:
|
|
177
|
+
raise ValueError("object_key 不能为空")
|
|
178
|
+
|
|
179
|
+
if method.upper() not in ["GET", "PUT", "POST", "DELETE"]:
|
|
180
|
+
raise ValueError(f"不支持的HTTP方法: {method}")
|
|
181
|
+
|
|
182
|
+
if expires_in <= 0 or expires_in > 604800: # 最大7天
|
|
183
|
+
raise ValueError("过期时间必须在1秒到604800秒(7天)之间")
|
|
184
|
+
|
|
185
|
+
# 生成预签名URL
|
|
186
|
+
from datetime import datetime, timedelta
|
|
187
|
+
|
|
188
|
+
if method.upper() == "GET":
|
|
189
|
+
url = self.bucket.sign_url(
|
|
190
|
+
method='GET',
|
|
191
|
+
key=object_key,
|
|
192
|
+
expires=expires_in,
|
|
193
|
+
headers=response_headers
|
|
194
|
+
)
|
|
195
|
+
elif method.upper() == "PUT":
|
|
196
|
+
url = self.bucket.sign_url(
|
|
197
|
+
method='PUT',
|
|
198
|
+
key=object_key,
|
|
199
|
+
expires=expires_in
|
|
200
|
+
)
|
|
201
|
+
elif method.upper() == "POST":
|
|
202
|
+
url = self.bucket.sign_url(
|
|
203
|
+
method='POST',
|
|
204
|
+
key=object_key,
|
|
205
|
+
expires=expires_in
|
|
206
|
+
)
|
|
207
|
+
elif method.upper() == "DELETE":
|
|
208
|
+
url = self.bucket.sign_url(
|
|
209
|
+
method='DELETE',
|
|
210
|
+
key=object_key,
|
|
211
|
+
expires=expires_in
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
logger.info(f"生成预签名URL成功: {object_key}, method: {method}")
|
|
215
|
+
return url
|
|
216
|
+
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logger.error(f"生成预签名URL失败: {str(e)}")
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def generate_presigned_doc_url(self, filename: str, method: str = "GET", expires_in: int = 3600,
|
|
222
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
223
|
+
auth_tenant_id: Optional[str] = None,
|
|
224
|
+
auth_company_id: Optional[str] = None,
|
|
225
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
226
|
+
"""
|
|
227
|
+
生成单据文件的预签名URL
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
filename: 文件名
|
|
231
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
232
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
233
|
+
response_headers: 响应头设置
|
|
234
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
235
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
236
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
# 构建单据文件路径
|
|
243
|
+
object_key = self._build_doc_path(filename, auth_tenant_id, auth_company_id, doc_id)
|
|
244
|
+
|
|
245
|
+
# 调用通用预签名URL生成方法
|
|
246
|
+
return self.generate_presigned_url(
|
|
247
|
+
object_key=object_key,
|
|
248
|
+
method=method,
|
|
249
|
+
expires_in=expires_in,
|
|
250
|
+
response_headers=response_headers
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
except Exception as e:
|
|
254
|
+
logger.error(f"生成单据文件预签名URL失败: {str(e)}")
|
|
255
|
+
return None
|
|
@@ -12,7 +12,7 @@ from minio import Minio
|
|
|
12
12
|
from minio.error import S3Error
|
|
13
13
|
from minio.commonconfig import Tags
|
|
14
14
|
from ..object_storage import ObjectStorage, StorageConfig
|
|
15
|
-
from ..type import DocFileObject
|
|
15
|
+
from ..type import DocFileObject
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
@@ -186,8 +186,6 @@ class MinioStorage(ObjectStorage):
|
|
|
186
186
|
def upload_doc_file(self,
|
|
187
187
|
filename: str,
|
|
188
188
|
data: bytes,
|
|
189
|
-
doc_source_from: Optional[DocSourceFrom] = None,
|
|
190
|
-
doc_file_type: Optional[DocFileType] = None,
|
|
191
189
|
tags: Optional[Dict[str, str]] = None,
|
|
192
190
|
auth_tenant_id: Optional[str] = None,
|
|
193
191
|
auth_company_id: Optional[str] = None,
|
|
@@ -202,11 +200,6 @@ class MinioStorage(ObjectStorage):
|
|
|
202
200
|
if tags:
|
|
203
201
|
for k, v in tags.items():
|
|
204
202
|
tags_obj[k] = v
|
|
205
|
-
|
|
206
|
-
doc_source_from = doc_source_from if doc_source_from else DocSourceFrom.OTHER
|
|
207
|
-
doc_file_type = doc_file_type if doc_file_type else DocFileType.OTHER
|
|
208
|
-
tags_obj["doc_source_from"] = doc_source_from.value
|
|
209
|
-
tags_obj["doc_file_type"] = doc_file_type.value
|
|
210
203
|
|
|
211
204
|
self.client.put_object(
|
|
212
205
|
bucket_name=self.bucket_name,
|
|
@@ -264,4 +257,110 @@ class MinioStorage(ObjectStorage):
|
|
|
264
257
|
return files
|
|
265
258
|
except S3Error as e:
|
|
266
259
|
logger.error(f"list doc files failed: {str(e)}")
|
|
267
|
-
return []
|
|
260
|
+
return []
|
|
261
|
+
|
|
262
|
+
def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
|
|
263
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
264
|
+
auth_tenant_id: Optional[str] = None,
|
|
265
|
+
auth_company_id: Optional[str] = None,
|
|
266
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
267
|
+
"""
|
|
268
|
+
生成预签名URL
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
object_key: 对象存储中的key
|
|
272
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
273
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
274
|
+
response_headers: 响应头设置
|
|
275
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
276
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
277
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
# 验证参数
|
|
284
|
+
if not object_key:
|
|
285
|
+
raise ValueError("object_key 不能为空")
|
|
286
|
+
|
|
287
|
+
if method.upper() not in ["GET", "PUT", "POST", "DELETE"]:
|
|
288
|
+
raise ValueError(f"不支持的HTTP方法: {method}")
|
|
289
|
+
|
|
290
|
+
if expires_in <= 0 or expires_in > 604800: # 最大7天
|
|
291
|
+
raise ValueError("过期时间必须在1秒到604800秒(7天)之间")
|
|
292
|
+
|
|
293
|
+
# 生成预签名URL
|
|
294
|
+
from datetime import timedelta
|
|
295
|
+
|
|
296
|
+
if method.upper() == "GET":
|
|
297
|
+
url = self.client.presigned_get_object(
|
|
298
|
+
bucket_name=self.bucket_name,
|
|
299
|
+
object_name=object_key,
|
|
300
|
+
expires=timedelta(seconds=expires_in),
|
|
301
|
+
response_headers=response_headers
|
|
302
|
+
)
|
|
303
|
+
elif method.upper() == "PUT":
|
|
304
|
+
url = self.client.presigned_put_object(
|
|
305
|
+
bucket_name=self.bucket_name,
|
|
306
|
+
object_name=object_key,
|
|
307
|
+
expires=timedelta(seconds=expires_in)
|
|
308
|
+
)
|
|
309
|
+
elif method.upper() == "POST":
|
|
310
|
+
url = self.client.presigned_post_policy(
|
|
311
|
+
bucket_name=self.bucket_name,
|
|
312
|
+
object_name=object_key,
|
|
313
|
+
expires=timedelta(seconds=expires_in)
|
|
314
|
+
)
|
|
315
|
+
elif method.upper() == "DELETE":
|
|
316
|
+
url = self.client.presigned_delete_object(
|
|
317
|
+
bucket_name=self.bucket_name,
|
|
318
|
+
object_name=object_key,
|
|
319
|
+
expires=timedelta(seconds=expires_in)
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
logger.info(f"生成预签名URL成功: {object_key}, method: {method}")
|
|
323
|
+
return url
|
|
324
|
+
|
|
325
|
+
except S3Error as e:
|
|
326
|
+
logger.error(f"生成预签名URL失败: {str(e)}")
|
|
327
|
+
return None
|
|
328
|
+
except Exception as e:
|
|
329
|
+
logger.error(f"生成预签名URL异常: {str(e)}")
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
def generate_presigned_doc_url(self, filename: str, method: str = "GET", expires_in: int = 3600,
|
|
333
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
334
|
+
auth_tenant_id: Optional[str] = None,
|
|
335
|
+
auth_company_id: Optional[str] = None,
|
|
336
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
337
|
+
"""
|
|
338
|
+
生成单据文件的预签名URL
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
filename: 文件名
|
|
342
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
343
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
344
|
+
response_headers: 响应头设置
|
|
345
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
346
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
347
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
351
|
+
"""
|
|
352
|
+
try:
|
|
353
|
+
# 构建单据文件路径
|
|
354
|
+
object_key = self._build_doc_path(filename, auth_tenant_id, auth_company_id, doc_id)
|
|
355
|
+
|
|
356
|
+
# 调用通用预签名URL生成方法
|
|
357
|
+
return self.generate_presigned_url(
|
|
358
|
+
object_key=object_key,
|
|
359
|
+
method=method,
|
|
360
|
+
expires_in=expires_in,
|
|
361
|
+
response_headers=response_headers
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
except Exception as e:
|
|
365
|
+
logger.error(f"生成单据文件预签名URL失败: {str(e)}")
|
|
366
|
+
return None
|
|
@@ -120,8 +120,6 @@ class ObjectStorage(ABC):
|
|
|
120
120
|
def upload_doc_file(self,
|
|
121
121
|
filename: str,
|
|
122
122
|
data: bytes,
|
|
123
|
-
doc_source_from: Optional[DocSourceFrom] = None,
|
|
124
|
-
doc_file_type: Optional[DocFileType] = None,
|
|
125
123
|
tags: Optional[Dict[str, str]] = None,
|
|
126
124
|
auth_tenant_id: Optional[str] = None, auth_company_id: Optional[str] = None, doc_id: Optional[str] = None) -> bool:
|
|
127
125
|
"""
|
|
@@ -165,6 +163,52 @@ class ObjectStorage(ABC):
|
|
|
165
163
|
"""
|
|
166
164
|
pass
|
|
167
165
|
|
|
166
|
+
@abstractmethod
|
|
167
|
+
def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
|
|
168
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
169
|
+
auth_tenant_id: Optional[str] = None,
|
|
170
|
+
auth_company_id: Optional[str] = None,
|
|
171
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
172
|
+
"""
|
|
173
|
+
生成预签名URL
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
object_key: 对象存储中的key,如果为空则使用单据路径
|
|
177
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
178
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
179
|
+
response_headers: 响应头设置
|
|
180
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
181
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
182
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
186
|
+
"""
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
@abstractmethod
|
|
190
|
+
def generate_presigned_doc_url(self, filename: str, method: str = "GET", expires_in: int = 3600,
|
|
191
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
192
|
+
auth_tenant_id: Optional[str] = None,
|
|
193
|
+
auth_company_id: Optional[str] = None,
|
|
194
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
195
|
+
"""
|
|
196
|
+
生成单据文件的预签名URL
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
filename: 文件名
|
|
200
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
201
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
202
|
+
response_headers: 响应头设置
|
|
203
|
+
auth_tenant_id: 租户ID(可选,若不传则用实例属性)
|
|
204
|
+
auth_company_id: 公司ID(可选,若不传则用实例属性)
|
|
205
|
+
doc_id: 单据ID(可选,若不传则用实例属性)
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
209
|
+
"""
|
|
210
|
+
pass
|
|
211
|
+
|
|
168
212
|
class ObjectStorageFactory:
|
|
169
213
|
"""对象存储工厂类"""
|
|
170
214
|
|
|
@@ -255,6 +299,68 @@ class ObjectStorageFactory:
|
|
|
255
299
|
return MinioStorage(config, auth_tenant_id, auth_company_id, doc_id)
|
|
256
300
|
else:
|
|
257
301
|
raise ValueError(f"不支持的存储提供商: {provider}")
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def generate_presigned_url(cls, object_key: str, method: str = "GET", expires_in: int = 3600,
|
|
305
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
306
|
+
auth_tenant_id: Optional[str] = None,
|
|
307
|
+
auth_company_id: Optional[str] = None,
|
|
308
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
309
|
+
"""通过工厂类生成预签名URL
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
object_key: 对象存储中的key
|
|
313
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
314
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
315
|
+
response_headers: 响应头设置
|
|
316
|
+
auth_tenant_id: 租户ID(可选)
|
|
317
|
+
auth_company_id: 公司ID(可选)
|
|
318
|
+
doc_id: 单据ID(可选)
|
|
319
|
+
|
|
320
|
+
Returns:
|
|
321
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
322
|
+
"""
|
|
323
|
+
storage = cls.get_instance()
|
|
324
|
+
return storage.generate_presigned_url(
|
|
325
|
+
object_key=object_key,
|
|
326
|
+
method=method,
|
|
327
|
+
expires_in=expires_in,
|
|
328
|
+
response_headers=response_headers,
|
|
329
|
+
auth_tenant_id=auth_tenant_id,
|
|
330
|
+
auth_company_id=auth_company_id,
|
|
331
|
+
doc_id=doc_id
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
@classmethod
|
|
335
|
+
def generate_presigned_doc_url(cls, filename: str, method: str = "GET", expires_in: int = 3600,
|
|
336
|
+
response_headers: Optional[Dict[str, str]] = None,
|
|
337
|
+
auth_tenant_id: Optional[str] = None,
|
|
338
|
+
auth_company_id: Optional[str] = None,
|
|
339
|
+
doc_id: Optional[str] = None) -> Optional[str]:
|
|
340
|
+
"""通过工厂类生成单据文件的预签名URL
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
filename: 文件名
|
|
344
|
+
method: HTTP方法,支持 GET、PUT、POST、DELETE
|
|
345
|
+
expires_in: 过期时间(秒),默认3600秒(1小时)
|
|
346
|
+
response_headers: 响应头设置
|
|
347
|
+
auth_tenant_id: 租户ID(可选)
|
|
348
|
+
auth_company_id: 公司ID(可选)
|
|
349
|
+
doc_id: 单据ID(可选)
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Optional[str]: 预签名URL,失败时返回None
|
|
353
|
+
"""
|
|
354
|
+
storage = cls.get_instance()
|
|
355
|
+
return storage.generate_presigned_doc_url(
|
|
356
|
+
filename=filename,
|
|
357
|
+
method=method,
|
|
358
|
+
expires_in=expires_in,
|
|
359
|
+
response_headers=response_headers,
|
|
360
|
+
auth_tenant_id=auth_tenant_id,
|
|
361
|
+
auth_company_id=auth_company_id,
|
|
362
|
+
doc_id=doc_id
|
|
363
|
+
)
|
|
258
364
|
|
|
259
365
|
# 导出工厂类
|
|
260
366
|
__all__ = ['ObjectStorage', 'ObjectStorageFactory', 'StorageConfig']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|