fiuai-s3 0.4.1__py3-none-any.whl → 0.4.2__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 fiuai-s3 might be problematic. Click here for more details.
- fiuai_s3/__init__.py +1 -1
- fiuai_s3/alicloud/alicloud_storage.py +104 -1
- fiuai_s3/minio/minio_storage.py +107 -1
- fiuai_s3/object_storage.py +108 -0
- {fiuai_s3-0.4.1.dist-info → fiuai_s3-0.4.2.dist-info}/METADATA +1 -1
- fiuai_s3-0.4.2.dist-info/RECORD +11 -0
- fiuai_s3-0.4.1.dist-info/RECORD +0 -11
- {fiuai_s3-0.4.1.dist-info → fiuai_s3-0.4.2.dist-info}/WHEEL +0 -0
- {fiuai_s3-0.4.1.dist-info → fiuai_s3-0.4.2.dist-info}/licenses/LICENSE +0 -0
fiuai_s3/__init__.py
CHANGED
|
@@ -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
|
fiuai_s3/minio/minio_storage.py
CHANGED
|
@@ -257,4 +257,110 @@ class MinioStorage(ObjectStorage):
|
|
|
257
257
|
return files
|
|
258
258
|
except S3Error as e:
|
|
259
259
|
logger.error(f"list doc files failed: {str(e)}")
|
|
260
|
-
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
|
fiuai_s3/object_storage.py
CHANGED
|
@@ -163,6 +163,52 @@ class ObjectStorage(ABC):
|
|
|
163
163
|
"""
|
|
164
164
|
pass
|
|
165
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
|
+
|
|
166
212
|
class ObjectStorageFactory:
|
|
167
213
|
"""对象存储工厂类"""
|
|
168
214
|
|
|
@@ -253,6 +299,68 @@ class ObjectStorageFactory:
|
|
|
253
299
|
return MinioStorage(config, auth_tenant_id, auth_company_id, doc_id)
|
|
254
300
|
else:
|
|
255
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
|
+
)
|
|
256
364
|
|
|
257
365
|
# 导出工厂类
|
|
258
366
|
__all__ = ['ObjectStorage', 'ObjectStorageFactory', 'StorageConfig']
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
fiuai_s3/__init__.py,sha256=BZvQEEefO33hqV8ZcT0HSLtW0XKsw8KpoxBinHTIlzo,421
|
|
2
|
+
fiuai_s3/object_storage.py,sha256=1vPiFFK1eb9_tF-fSn2_CBqpWtbm5WsgSvGk3VEOvvo,14169
|
|
3
|
+
fiuai_s3/type.py,sha256=L1h3SQh7_NhWfjWPa1MMgGoxM7g6wQF26qg5WsERt6U,981
|
|
4
|
+
fiuai_s3/alicloud/__init__.py,sha256=mmrCrFp5DzRF5fViJDq7_LpsqCViwTPXOPz4qoaSscI,218
|
|
5
|
+
fiuai_s3/alicloud/alicloud_storage.py,sha256=DieaILMmPXYU8-xbZIZbdkmju3PXEVDWXAQ7qAtuJdg,10417
|
|
6
|
+
fiuai_s3/minio/__init__.py,sha256=hOmpUkTq8TPgYOxfeOMcWq1_YsJ2r8Zf9VTX3w_v1Lk,209
|
|
7
|
+
fiuai_s3/minio/minio_storage.py,sha256=z_LvuL_CGr0bisCF9vYGwHdqJbohYi9qQFevGV4JhyM,14281
|
|
8
|
+
fiuai_s3-0.4.2.dist-info/METADATA,sha256=0KxOv7GE5LS5N0zPxcTvx63oAR2dP0FjhSHpeMNc8XE,18727
|
|
9
|
+
fiuai_s3-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
+
fiuai_s3-0.4.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
+
fiuai_s3-0.4.2.dist-info/RECORD,,
|
fiuai_s3-0.4.1.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
fiuai_s3/__init__.py,sha256=tLlqnMEl4K7R4jg2GbvGZanRsis1vm7KFKSc5R_wRUE,421
|
|
2
|
-
fiuai_s3/object_storage.py,sha256=jbXQJ8SBZNMoLQz4OXs6vWwY1cVON2d_5a3LolzcqD8,9335
|
|
3
|
-
fiuai_s3/type.py,sha256=L1h3SQh7_NhWfjWPa1MMgGoxM7g6wQF26qg5WsERt6U,981
|
|
4
|
-
fiuai_s3/alicloud/__init__.py,sha256=mmrCrFp5DzRF5fViJDq7_LpsqCViwTPXOPz4qoaSscI,218
|
|
5
|
-
fiuai_s3/alicloud/alicloud_storage.py,sha256=d24AFYDs7RsTsfL9IiVOxYlGyLzsaUa_WUYD5VPLP4s,6035
|
|
6
|
-
fiuai_s3/minio/__init__.py,sha256=hOmpUkTq8TPgYOxfeOMcWq1_YsJ2r8Zf9VTX3w_v1Lk,209
|
|
7
|
-
fiuai_s3/minio/minio_storage.py,sha256=k0IKpifeUP8JwfDCry0yuFTzrnaHA9ZWYQToNhGS99s,9563
|
|
8
|
-
fiuai_s3-0.4.1.dist-info/METADATA,sha256=0ye44O-YuXc3ZCwvvUBRDQdr7f-owyyp3FHQLaa5Yf4,18727
|
|
9
|
-
fiuai_s3-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
-
fiuai_s3-0.4.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
11
|
-
fiuai_s3-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|