fiuai-s3 0.4.1__tar.gz → 0.4.3__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fiuai-s3
3
- Version: 0.4.1
3
+ Version: 0.4.3
4
4
  Summary: 一个支持阿里云OSS和MinIO的对象存储抽象包
5
5
  Project-URL: Homepage, https://github.com/fiuai-sz/fiuai-s3
6
6
  Project-URL: Repository, https://github.com/fiuai-sz/fiuai-s3.git
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fiuai-s3"
7
- version = "0.4.1"
7
+ version = "0.4.3"
8
8
  description = "一个支持阿里云OSS和MinIO的对象存储抽象包"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -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.3.9"
11
+ __version__ = "0.4.1"
12
12
  __all__ = ["ObjectStorage", "ObjectStorageFactory", "StorageConfig", "DocFileObject", "DocSourceFrom", "DocFileType"]
@@ -7,7 +7,7 @@
7
7
 
8
8
  import os
9
9
  import logging
10
- from typing import List, Optional
10
+ from typing import List, Optional, Dict
11
11
  import oss2
12
12
  from ..object_storage import ObjectStorage, StorageConfig
13
13
  from oss2.headers import OSS_OBJECT_TAGGING
@@ -37,6 +37,32 @@ class AliCloudStorage(ObjectStorage):
37
37
  bucket_name=config.bucket_name
38
38
  )
39
39
 
40
+ def upload_temp_file(self, object_key: str, data: bytes, tmppath: str = None) -> bool:
41
+ """上传临时文件到阿里云OSS
42
+
43
+ Args:
44
+ object_key: 对象存储中的key
45
+ data: 文件数据
46
+ tmppath: 临时文件路径, 如果为空,则使用默认临时目录
47
+ Returns:
48
+ bool: 是否上传成功
49
+ """
50
+ _path = f"{self.config.temp_dir}/{object_key}" if not tmppath else f"{tmppath.rstrip('/')}/{object_key}"
51
+ return self.upload_file(_path, data)
52
+
53
+ def download_temp_file(self, object_key: str, tmppath: str = None) -> bytes:
54
+ """从阿里云OSS下载临时文件
55
+
56
+ Args:
57
+ object_key: 对象存储中的key
58
+ tmppath: 临时文件路径, 如果为空,则使用默认临时目录
59
+
60
+ Returns:
61
+ bytes: 文件内容,失败时返回None
62
+ """
63
+ _path = f"{self.config.temp_dir}/{object_key}" if not tmppath else f"{tmppath.rstrip('/')}/{object_key}"
64
+ return self.download_file(_path)
65
+
40
66
  def upload_file(self, object_key: str, data: bytes) -> bool:
41
67
  """上传文件到阿里云OSS
42
68
 
@@ -149,4 +175,107 @@ class AliCloudStorage(ObjectStorage):
149
175
  return files
150
176
  except Exception as e:
151
177
  logger.error(f"列出单据文件失败: {str(e)}")
152
- return []
178
+ return []
179
+
180
+ def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
181
+ response_headers: Optional[Dict[str, str]] = None,
182
+ auth_tenant_id: Optional[str] = None,
183
+ auth_company_id: Optional[str] = None,
184
+ doc_id: Optional[str] = None) -> Optional[str]:
185
+ """
186
+ 生成预签名URL
187
+
188
+ Args:
189
+ object_key: 对象存储中的key
190
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
191
+ expires_in: 过期时间(秒),默认3600秒(1小时)
192
+ response_headers: 响应头设置
193
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
194
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
195
+ doc_id: 单据ID(可选,若不传则用实例属性)
196
+
197
+ Returns:
198
+ Optional[str]: 预签名URL,失败时返回None
199
+ """
200
+ try:
201
+ # 验证参数
202
+ if not object_key:
203
+ raise ValueError("object_key 不能为空")
204
+
205
+ if method.upper() not in ["GET", "PUT", "POST", "DELETE"]:
206
+ raise ValueError(f"不支持的HTTP方法: {method}")
207
+
208
+ if expires_in <= 0 or expires_in > 604800: # 最大7天
209
+ raise ValueError("过期时间必须在1秒到604800秒(7天)之间")
210
+
211
+ # 生成预签名URL
212
+ from datetime import datetime, timedelta
213
+
214
+ if method.upper() == "GET":
215
+ url = self.bucket.sign_url(
216
+ method='GET',
217
+ key=object_key,
218
+ expires=expires_in,
219
+ headers=response_headers
220
+ )
221
+ elif method.upper() == "PUT":
222
+ url = self.bucket.sign_url(
223
+ method='PUT',
224
+ key=object_key,
225
+ expires=expires_in
226
+ )
227
+ elif method.upper() == "POST":
228
+ url = self.bucket.sign_url(
229
+ method='POST',
230
+ key=object_key,
231
+ expires=expires_in
232
+ )
233
+ elif method.upper() == "DELETE":
234
+ url = self.bucket.sign_url(
235
+ method='DELETE',
236
+ key=object_key,
237
+ expires=expires_in
238
+ )
239
+
240
+ logger.info(f"生成预签名URL成功: {object_key}, method: {method}")
241
+ return url
242
+
243
+ except Exception as e:
244
+ logger.error(f"生成预签名URL失败: {str(e)}")
245
+ return None
246
+
247
+ def generate_presigned_doc_url(self, filename: str, method: str = "GET", expires_in: int = 3600,
248
+ response_headers: Optional[Dict[str, str]] = None,
249
+ auth_tenant_id: Optional[str] = None,
250
+ auth_company_id: Optional[str] = None,
251
+ doc_id: Optional[str] = None) -> Optional[str]:
252
+ """
253
+ 生成单据文件的预签名URL
254
+
255
+ Args:
256
+ filename: 文件名
257
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
258
+ expires_in: 过期时间(秒),默认3600秒(1小时)
259
+ response_headers: 响应头设置
260
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
261
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
262
+ doc_id: 单据ID(可选,若不传则用实例属性)
263
+
264
+ Returns:
265
+ Optional[str]: 预签名URL,失败时返回None
266
+ """
267
+ try:
268
+ # 构建单据文件路径
269
+ object_key = self._build_doc_path(filename, auth_tenant_id, auth_company_id, doc_id)
270
+
271
+ # 调用通用预签名URL生成方法
272
+ return self.generate_presigned_url(
273
+ object_key=object_key,
274
+ method=method,
275
+ expires_in=expires_in,
276
+ response_headers=response_headers
277
+ )
278
+
279
+ except Exception as e:
280
+ logger.error(f"生成单据文件预签名URL失败: {str(e)}")
281
+ return None
@@ -81,12 +81,15 @@ class MinioStorage(ObjectStorage):
81
81
  _path = f"{self.config.temp_dir}/{object_key}" if not tmppath else f"{tmppath.rstrip('/')}/{object_key}"
82
82
  return self.upload_file(_path, data)
83
83
 
84
- def download_temp_file(self, object_key: str, tmppath: str = None) -> bool:
84
+ def download_temp_file(self, object_key: str, tmppath: str = None) -> bytes:
85
85
  """下载临时文件
86
86
 
87
87
  Args:
88
88
  object_key: 对象存储中的key
89
89
  tmppath: 临时文件路径, 如果为空,则使用默认临时目录
90
+
91
+ Returns:
92
+ bytes: 文件内容,失败时返回None
90
93
  """
91
94
  _path = f"{self.config.temp_dir}/{object_key}" if not tmppath else f"{tmppath.rstrip('/')}/{object_key}"
92
95
  return self.download_file(_path)
@@ -257,4 +260,110 @@ class MinioStorage(ObjectStorage):
257
260
  return files
258
261
  except S3Error as e:
259
262
  logger.error(f"list doc files failed: {str(e)}")
260
- return []
263
+ return []
264
+
265
+ def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
266
+ response_headers: Optional[Dict[str, str]] = None,
267
+ auth_tenant_id: Optional[str] = None,
268
+ auth_company_id: Optional[str] = None,
269
+ doc_id: Optional[str] = None) -> Optional[str]:
270
+ """
271
+ 生成预签名URL
272
+
273
+ Args:
274
+ object_key: 对象存储中的key
275
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
276
+ expires_in: 过期时间(秒),默认3600秒(1小时)
277
+ response_headers: 响应头设置
278
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
279
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
280
+ doc_id: 单据ID(可选,若不传则用实例属性)
281
+
282
+ Returns:
283
+ Optional[str]: 预签名URL,失败时返回None
284
+ """
285
+ try:
286
+ # 验证参数
287
+ if not object_key:
288
+ raise ValueError("object_key 不能为空")
289
+
290
+ if method.upper() not in ["GET", "PUT", "POST", "DELETE"]:
291
+ raise ValueError(f"不支持的HTTP方法: {method}")
292
+
293
+ if expires_in <= 0 or expires_in > 604800: # 最大7天
294
+ raise ValueError("过期时间必须在1秒到604800秒(7天)之间")
295
+
296
+ # 生成预签名URL
297
+ from datetime import timedelta
298
+
299
+ if method.upper() == "GET":
300
+ url = self.client.presigned_get_object(
301
+ bucket_name=self.bucket_name,
302
+ object_name=object_key,
303
+ expires=timedelta(seconds=expires_in),
304
+ response_headers=response_headers
305
+ )
306
+ elif method.upper() == "PUT":
307
+ url = self.client.presigned_put_object(
308
+ bucket_name=self.bucket_name,
309
+ object_name=object_key,
310
+ expires=timedelta(seconds=expires_in)
311
+ )
312
+ elif method.upper() == "POST":
313
+ url = self.client.presigned_post_policy(
314
+ bucket_name=self.bucket_name,
315
+ object_name=object_key,
316
+ expires=timedelta(seconds=expires_in)
317
+ )
318
+ elif method.upper() == "DELETE":
319
+ url = self.client.presigned_delete_object(
320
+ bucket_name=self.bucket_name,
321
+ object_name=object_key,
322
+ expires=timedelta(seconds=expires_in)
323
+ )
324
+
325
+ logger.info(f"生成预签名URL成功: {object_key}, method: {method}")
326
+ return url
327
+
328
+ except S3Error as e:
329
+ logger.error(f"生成预签名URL失败: {str(e)}")
330
+ return None
331
+ except Exception as e:
332
+ logger.error(f"生成预签名URL异常: {str(e)}")
333
+ return None
334
+
335
+ def generate_presigned_doc_url(self, 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
+ """
341
+ 生成单据文件的预签名URL
342
+
343
+ Args:
344
+ filename: 文件名
345
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
346
+ expires_in: 过期时间(秒),默认3600秒(1小时)
347
+ response_headers: 响应头设置
348
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
349
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
350
+ doc_id: 单据ID(可选,若不传则用实例属性)
351
+
352
+ Returns:
353
+ Optional[str]: 预签名URL,失败时返回None
354
+ """
355
+ try:
356
+ # 构建单据文件路径
357
+ object_key = self._build_doc_path(filename, auth_tenant_id, auth_company_id, doc_id)
358
+
359
+ # 调用通用预签名URL生成方法
360
+ return self.generate_presigned_url(
361
+ object_key=object_key,
362
+ method=method,
363
+ expires_in=expires_in,
364
+ response_headers=response_headers
365
+ )
366
+
367
+ except Exception as e:
368
+ logger.error(f"生成单据文件预签名URL失败: {str(e)}")
369
+ return None
@@ -58,12 +58,15 @@ class ObjectStorage(ABC):
58
58
  pass
59
59
 
60
60
  @abstractmethod
61
- def download_temp_file(self, object_key: str, tmppath: str = None) -> bool:
61
+ def download_temp_file(self, object_key: str, tmppath: str = None) -> bytes:
62
62
  """下载临时文件
63
63
 
64
64
  Args:
65
65
  object_key: 对象存储中的key
66
66
  tmppath: 临时文件路径, 如果为空,则使用默认临时目录
67
+
68
+ Returns:
69
+ bytes: 文件内容,失败时返回None
67
70
  """
68
71
  pass
69
72
 
@@ -163,6 +166,52 @@ class ObjectStorage(ABC):
163
166
  """
164
167
  pass
165
168
 
169
+ @abstractmethod
170
+ def generate_presigned_url(self, object_key: str, method: str = "GET", expires_in: int = 3600,
171
+ response_headers: Optional[Dict[str, str]] = None,
172
+ auth_tenant_id: Optional[str] = None,
173
+ auth_company_id: Optional[str] = None,
174
+ doc_id: Optional[str] = None) -> Optional[str]:
175
+ """
176
+ 生成预签名URL
177
+
178
+ Args:
179
+ object_key: 对象存储中的key,如果为空则使用单据路径
180
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
181
+ expires_in: 过期时间(秒),默认3600秒(1小时)
182
+ response_headers: 响应头设置
183
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
184
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
185
+ doc_id: 单据ID(可选,若不传则用实例属性)
186
+
187
+ Returns:
188
+ Optional[str]: 预签名URL,失败时返回None
189
+ """
190
+ pass
191
+
192
+ @abstractmethod
193
+ def generate_presigned_doc_url(self, filename: str, method: str = "GET", expires_in: int = 3600,
194
+ response_headers: Optional[Dict[str, str]] = None,
195
+ auth_tenant_id: Optional[str] = None,
196
+ auth_company_id: Optional[str] = None,
197
+ doc_id: Optional[str] = None) -> Optional[str]:
198
+ """
199
+ 生成单据文件的预签名URL
200
+
201
+ Args:
202
+ filename: 文件名
203
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
204
+ expires_in: 过期时间(秒),默认3600秒(1小时)
205
+ response_headers: 响应头设置
206
+ auth_tenant_id: 租户ID(可选,若不传则用实例属性)
207
+ auth_company_id: 公司ID(可选,若不传则用实例属性)
208
+ doc_id: 单据ID(可选,若不传则用实例属性)
209
+
210
+ Returns:
211
+ Optional[str]: 预签名URL,失败时返回None
212
+ """
213
+ pass
214
+
166
215
  class ObjectStorageFactory:
167
216
  """对象存储工厂类"""
168
217
 
@@ -253,6 +302,68 @@ class ObjectStorageFactory:
253
302
  return MinioStorage(config, auth_tenant_id, auth_company_id, doc_id)
254
303
  else:
255
304
  raise ValueError(f"不支持的存储提供商: {provider}")
305
+
306
+ @classmethod
307
+ def generate_presigned_url(cls, object_key: str, method: str = "GET", expires_in: int = 3600,
308
+ response_headers: Optional[Dict[str, str]] = None,
309
+ auth_tenant_id: Optional[str] = None,
310
+ auth_company_id: Optional[str] = None,
311
+ doc_id: Optional[str] = None) -> Optional[str]:
312
+ """通过工厂类生成预签名URL
313
+
314
+ Args:
315
+ object_key: 对象存储中的key
316
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
317
+ expires_in: 过期时间(秒),默认3600秒(1小时)
318
+ response_headers: 响应头设置
319
+ auth_tenant_id: 租户ID(可选)
320
+ auth_company_id: 公司ID(可选)
321
+ doc_id: 单据ID(可选)
322
+
323
+ Returns:
324
+ Optional[str]: 预签名URL,失败时返回None
325
+ """
326
+ storage = cls.get_instance()
327
+ return storage.generate_presigned_url(
328
+ object_key=object_key,
329
+ method=method,
330
+ expires_in=expires_in,
331
+ response_headers=response_headers,
332
+ auth_tenant_id=auth_tenant_id,
333
+ auth_company_id=auth_company_id,
334
+ doc_id=doc_id
335
+ )
336
+
337
+ @classmethod
338
+ def generate_presigned_doc_url(cls, filename: str, method: str = "GET", expires_in: int = 3600,
339
+ response_headers: Optional[Dict[str, str]] = None,
340
+ auth_tenant_id: Optional[str] = None,
341
+ auth_company_id: Optional[str] = None,
342
+ doc_id: Optional[str] = None) -> Optional[str]:
343
+ """通过工厂类生成单据文件的预签名URL
344
+
345
+ Args:
346
+ filename: 文件名
347
+ method: HTTP方法,支持 GET、PUT、POST、DELETE
348
+ expires_in: 过期时间(秒),默认3600秒(1小时)
349
+ response_headers: 响应头设置
350
+ auth_tenant_id: 租户ID(可选)
351
+ auth_company_id: 公司ID(可选)
352
+ doc_id: 单据ID(可选)
353
+
354
+ Returns:
355
+ Optional[str]: 预签名URL,失败时返回None
356
+ """
357
+ storage = cls.get_instance()
358
+ return storage.generate_presigned_doc_url(
359
+ filename=filename,
360
+ method=method,
361
+ expires_in=expires_in,
362
+ response_headers=response_headers,
363
+ auth_tenant_id=auth_tenant_id,
364
+ auth_company_id=auth_company_id,
365
+ doc_id=doc_id
366
+ )
256
367
 
257
368
  # 导出工厂类
258
369
  __all__ = ['ObjectStorage', 'ObjectStorageFactory', 'StorageConfig']
File without changes
File without changes
File without changes
File without changes