aod-oss-client 0.1.0__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.
- aod_oss_client-0.1.0/.env.example +10 -0
- aod_oss_client-0.1.0/.gitignore +10 -0
- aod_oss_client-0.1.0/LICENSE +21 -0
- aod_oss_client-0.1.0/PKG-INFO +198 -0
- aod_oss_client-0.1.0/README.md +173 -0
- aod_oss_client-0.1.0/docs/design.md +336 -0
- aod_oss_client-0.1.0/pyproject.toml +57 -0
- aod_oss_client-0.1.0/src/aod_oss_client/__init__.py +14 -0
- aod_oss_client-0.1.0/src/aod_oss_client/client.py +208 -0
- aod_oss_client-0.1.0/src/aod_oss_client/exceptions.py +13 -0
- aod_oss_client-0.1.0/src/aod_oss_client/models.py +43 -0
- aod_oss_client-0.1.0/src/aod_oss_client/oper.py +199 -0
- aod_oss_client-0.1.0/tests/__init__.py +2 -0
- aod_oss_client-0.1.0/tests/conftest.py +28 -0
- aod_oss_client-0.1.0/tests/fixtures/sample.txt +1 -0
- aod_oss_client-0.1.0/tests/test_client.py +104 -0
- aod_oss_client-0.1.0/tests/test_oper.py +49 -0
- aod_oss_client-0.1.0/tests/test_real_upload.py +124 -0
- aod_oss_client-0.1.0/uv.lock +844 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 aod-oss-client
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aod-oss-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 阿里云 OSS 上传客户端(同步 + 异步)
|
|
5
|
+
Author: aod-oss-client contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: aiofiles>=24.1
|
|
17
|
+
Requires-Dist: aiohttp>=3.9
|
|
18
|
+
Requires-Dist: alibabacloud-oss-v2>=1.0.0
|
|
19
|
+
Requires-Dist: python-dotenv>=1.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=9.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# aod-oss-client
|
|
27
|
+
|
|
28
|
+
阿里云 OSS 上传客户端,同时支持 **同步** 和 **异步** 两种接口。
|
|
29
|
+
|
|
30
|
+
## 特性
|
|
31
|
+
|
|
32
|
+
- 同步 + 异步双 API,同一 `OssClient` 实例可多次复用
|
|
33
|
+
- 自动选择简单上传(< 100MB)或分片上传(≥ 100MB)
|
|
34
|
+
- 分片并发上传,内置指数退避重试
|
|
35
|
+
- `content_type` 自动检测,不传自动推断
|
|
36
|
+
- `skip_if_exists` 支持幂等跳过
|
|
37
|
+
- 凭证支持显式传入 / 环境变量 / `.env` 文件
|
|
38
|
+
- 支持自定义域名、内网 Endpoint
|
|
39
|
+
|
|
40
|
+
## 安装
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install aod-oss-client
|
|
44
|
+
# 或
|
|
45
|
+
uv add aod-oss-client
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 快速开始
|
|
49
|
+
|
|
50
|
+
### 同步用法
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from aod_oss_client import OssClient, OssClientConfig
|
|
54
|
+
|
|
55
|
+
config = OssClientConfig(bucket="my-bucket", region="cn-hangzhou")
|
|
56
|
+
client = OssClient(config)
|
|
57
|
+
|
|
58
|
+
r1 = client.upload_file("/data/a.geojson", "data/a.geojson")
|
|
59
|
+
r2 = client.upload_file("/data/b.fgb", "data/b.fgb")
|
|
60
|
+
|
|
61
|
+
print(r1.url, r2.url)
|
|
62
|
+
# client.close() # 可选
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 异步用法
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from aod_oss_client import OssClient, OssClientConfig
|
|
69
|
+
|
|
70
|
+
config = OssClientConfig(bucket="my-bucket", region="cn-hangzhou")
|
|
71
|
+
client = OssClient(config)
|
|
72
|
+
|
|
73
|
+
r1 = await client.async_upload_file("/data/a.geojson", "data/a.geojson")
|
|
74
|
+
r2 = await client.async_upload_file("/data/b.fgb", "data/b.fgb")
|
|
75
|
+
|
|
76
|
+
print(r1.url, r2.url)
|
|
77
|
+
# await client.async_aclose() # 可选
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 幂等跳过
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
result = client.upload_file("photo.jpg", "images/photo.jpg", skip_if_exists=True)
|
|
84
|
+
if result.skipped:
|
|
85
|
+
print("文件已存在,跳过上传")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 自定义元属性
|
|
89
|
+
|
|
90
|
+
上传时可通过 `metadata` 参数设置 OSS 对象自定义元属性(即 `x-oss-meta-*` 头):
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
client.upload_file(
|
|
94
|
+
"data.geojson", "data.geojson",
|
|
95
|
+
metadata={
|
|
96
|
+
"source": "gps_collector",
|
|
97
|
+
"crs": "EPSG:4326",
|
|
98
|
+
"version": "2.1",
|
|
99
|
+
},
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
元属性会在 OSS 控制台和 GetObject 响应的 `x-oss-meta-*` 头中返回。
|
|
104
|
+
|
|
105
|
+
### content_type 自动检测
|
|
106
|
+
|
|
107
|
+
不传则根据文件扩展名自动推断:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
client.upload_file("data.json", "data.json") # → application/json
|
|
111
|
+
client.upload_file("data.geojson", "data.geojson") # → application/geo+json
|
|
112
|
+
client.upload_file("data.bin", "data.bin") # → application/octet-stream
|
|
113
|
+
client.upload_file("data.fgb", "data.fgb", content_type="application/geo+json") # 手动指定
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### with 语句
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
with OssClient(config) as client:
|
|
120
|
+
client.upload_file("a.txt", "a.txt")
|
|
121
|
+
client.upload_file("b.txt", "b.txt")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 配置
|
|
125
|
+
|
|
126
|
+
### OssClientConfig
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
config = OssClientConfig(
|
|
130
|
+
bucket="my-bucket",
|
|
131
|
+
region="cn-hangzhou",
|
|
132
|
+
access_key_id="your-key-id", # 可选,不传走环境变量
|
|
133
|
+
access_key_secret="your-key-secret",
|
|
134
|
+
endpoint=None, # 自定义 Endpoint
|
|
135
|
+
public_base_url=None, # 自定义域名
|
|
136
|
+
use_internal_endpoint=False, # 内网 Endpoint
|
|
137
|
+
multipart_threshold=100 * 1024 * 1024, # 100MB 分片阈值
|
|
138
|
+
part_size=10 * 1024 * 1024, # 10MB 分片大小
|
|
139
|
+
part_parallel=3, # 分片并发数
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 环境变量
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
OSS_ACCESS_KEY_ID=your_access_key_id
|
|
147
|
+
OSS_ACCESS_KEY_SECRET=your_access_key_secret
|
|
148
|
+
OSS_REGION=cn-hangzhou
|
|
149
|
+
OSS_BUCKET=my-bucket
|
|
150
|
+
OSS_ENDPOINT=
|
|
151
|
+
OSS_PUBLIC_BASE_URL=
|
|
152
|
+
OSS_USE_INTERNAL_ENDPOINT=false
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## API 参考
|
|
156
|
+
|
|
157
|
+
### OssClient
|
|
158
|
+
|
|
159
|
+
| 方法 | 说明 |
|
|
160
|
+
|------|------|
|
|
161
|
+
| `upload_file(local_path, object_key, ...)` | 同步上传 |
|
|
162
|
+
| `async_upload_file(local_path, object_key, ...)` | 异步上传 |
|
|
163
|
+
| `object_exists(object_key)` | 同步检查存在 |
|
|
164
|
+
| `async_object_exists(object_key)` | 异步检查存在 |
|
|
165
|
+
| `close()` | 同步关闭 |
|
|
166
|
+
| `async_aclose()` | 异步关闭 |
|
|
167
|
+
|
|
168
|
+
### UploadResult
|
|
169
|
+
|
|
170
|
+
| 字段 | 类型 | 说明 |
|
|
171
|
+
|------|------|------|
|
|
172
|
+
| `provider` | `str` | 云厂商标识 |
|
|
173
|
+
| `object_key` | `str` | OSS 对象键 |
|
|
174
|
+
| `url` | `str` | 公开访问 URL |
|
|
175
|
+
| `bytes` | `int` | 文件大小 |
|
|
176
|
+
| `etag` | `str | None` | OSS ETag |
|
|
177
|
+
| `skipped` | `bool` | 是否跳过上传 |
|
|
178
|
+
|
|
179
|
+
## 开发
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# 同步依赖
|
|
183
|
+
uv sync --all-extras
|
|
184
|
+
|
|
185
|
+
# 运行测试(单元测试)
|
|
186
|
+
uv run pytest
|
|
187
|
+
|
|
188
|
+
# 真实上传测试(需配置环境变量)
|
|
189
|
+
uv run python tests/test_real_upload.py
|
|
190
|
+
|
|
191
|
+
# 代码检查
|
|
192
|
+
uv run ruff check src
|
|
193
|
+
uv run ruff format src
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# aod-oss-client
|
|
2
|
+
|
|
3
|
+
阿里云 OSS 上传客户端,同时支持 **同步** 和 **异步** 两种接口。
|
|
4
|
+
|
|
5
|
+
## 特性
|
|
6
|
+
|
|
7
|
+
- 同步 + 异步双 API,同一 `OssClient` 实例可多次复用
|
|
8
|
+
- 自动选择简单上传(< 100MB)或分片上传(≥ 100MB)
|
|
9
|
+
- 分片并发上传,内置指数退避重试
|
|
10
|
+
- `content_type` 自动检测,不传自动推断
|
|
11
|
+
- `skip_if_exists` 支持幂等跳过
|
|
12
|
+
- 凭证支持显式传入 / 环境变量 / `.env` 文件
|
|
13
|
+
- 支持自定义域名、内网 Endpoint
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install aod-oss-client
|
|
19
|
+
# 或
|
|
20
|
+
uv add aod-oss-client
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 快速开始
|
|
24
|
+
|
|
25
|
+
### 同步用法
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from aod_oss_client import OssClient, OssClientConfig
|
|
29
|
+
|
|
30
|
+
config = OssClientConfig(bucket="my-bucket", region="cn-hangzhou")
|
|
31
|
+
client = OssClient(config)
|
|
32
|
+
|
|
33
|
+
r1 = client.upload_file("/data/a.geojson", "data/a.geojson")
|
|
34
|
+
r2 = client.upload_file("/data/b.fgb", "data/b.fgb")
|
|
35
|
+
|
|
36
|
+
print(r1.url, r2.url)
|
|
37
|
+
# client.close() # 可选
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 异步用法
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from aod_oss_client import OssClient, OssClientConfig
|
|
44
|
+
|
|
45
|
+
config = OssClientConfig(bucket="my-bucket", region="cn-hangzhou")
|
|
46
|
+
client = OssClient(config)
|
|
47
|
+
|
|
48
|
+
r1 = await client.async_upload_file("/data/a.geojson", "data/a.geojson")
|
|
49
|
+
r2 = await client.async_upload_file("/data/b.fgb", "data/b.fgb")
|
|
50
|
+
|
|
51
|
+
print(r1.url, r2.url)
|
|
52
|
+
# await client.async_aclose() # 可选
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 幂等跳过
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
result = client.upload_file("photo.jpg", "images/photo.jpg", skip_if_exists=True)
|
|
59
|
+
if result.skipped:
|
|
60
|
+
print("文件已存在,跳过上传")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 自定义元属性
|
|
64
|
+
|
|
65
|
+
上传时可通过 `metadata` 参数设置 OSS 对象自定义元属性(即 `x-oss-meta-*` 头):
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
client.upload_file(
|
|
69
|
+
"data.geojson", "data.geojson",
|
|
70
|
+
metadata={
|
|
71
|
+
"source": "gps_collector",
|
|
72
|
+
"crs": "EPSG:4326",
|
|
73
|
+
"version": "2.1",
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
元属性会在 OSS 控制台和 GetObject 响应的 `x-oss-meta-*` 头中返回。
|
|
79
|
+
|
|
80
|
+
### content_type 自动检测
|
|
81
|
+
|
|
82
|
+
不传则根据文件扩展名自动推断:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
client.upload_file("data.json", "data.json") # → application/json
|
|
86
|
+
client.upload_file("data.geojson", "data.geojson") # → application/geo+json
|
|
87
|
+
client.upload_file("data.bin", "data.bin") # → application/octet-stream
|
|
88
|
+
client.upload_file("data.fgb", "data.fgb", content_type="application/geo+json") # 手动指定
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### with 语句
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
with OssClient(config) as client:
|
|
95
|
+
client.upload_file("a.txt", "a.txt")
|
|
96
|
+
client.upload_file("b.txt", "b.txt")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 配置
|
|
100
|
+
|
|
101
|
+
### OssClientConfig
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
config = OssClientConfig(
|
|
105
|
+
bucket="my-bucket",
|
|
106
|
+
region="cn-hangzhou",
|
|
107
|
+
access_key_id="your-key-id", # 可选,不传走环境变量
|
|
108
|
+
access_key_secret="your-key-secret",
|
|
109
|
+
endpoint=None, # 自定义 Endpoint
|
|
110
|
+
public_base_url=None, # 自定义域名
|
|
111
|
+
use_internal_endpoint=False, # 内网 Endpoint
|
|
112
|
+
multipart_threshold=100 * 1024 * 1024, # 100MB 分片阈值
|
|
113
|
+
part_size=10 * 1024 * 1024, # 10MB 分片大小
|
|
114
|
+
part_parallel=3, # 分片并发数
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 环境变量
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
OSS_ACCESS_KEY_ID=your_access_key_id
|
|
122
|
+
OSS_ACCESS_KEY_SECRET=your_access_key_secret
|
|
123
|
+
OSS_REGION=cn-hangzhou
|
|
124
|
+
OSS_BUCKET=my-bucket
|
|
125
|
+
OSS_ENDPOINT=
|
|
126
|
+
OSS_PUBLIC_BASE_URL=
|
|
127
|
+
OSS_USE_INTERNAL_ENDPOINT=false
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API 参考
|
|
131
|
+
|
|
132
|
+
### OssClient
|
|
133
|
+
|
|
134
|
+
| 方法 | 说明 |
|
|
135
|
+
|------|------|
|
|
136
|
+
| `upload_file(local_path, object_key, ...)` | 同步上传 |
|
|
137
|
+
| `async_upload_file(local_path, object_key, ...)` | 异步上传 |
|
|
138
|
+
| `object_exists(object_key)` | 同步检查存在 |
|
|
139
|
+
| `async_object_exists(object_key)` | 异步检查存在 |
|
|
140
|
+
| `close()` | 同步关闭 |
|
|
141
|
+
| `async_aclose()` | 异步关闭 |
|
|
142
|
+
|
|
143
|
+
### UploadResult
|
|
144
|
+
|
|
145
|
+
| 字段 | 类型 | 说明 |
|
|
146
|
+
|------|------|------|
|
|
147
|
+
| `provider` | `str` | 云厂商标识 |
|
|
148
|
+
| `object_key` | `str` | OSS 对象键 |
|
|
149
|
+
| `url` | `str` | 公开访问 URL |
|
|
150
|
+
| `bytes` | `int` | 文件大小 |
|
|
151
|
+
| `etag` | `str | None` | OSS ETag |
|
|
152
|
+
| `skipped` | `bool` | 是否跳过上传 |
|
|
153
|
+
|
|
154
|
+
## 开发
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# 同步依赖
|
|
158
|
+
uv sync --all-extras
|
|
159
|
+
|
|
160
|
+
# 运行测试(单元测试)
|
|
161
|
+
uv run pytest
|
|
162
|
+
|
|
163
|
+
# 真实上传测试(需配置环境变量)
|
|
164
|
+
uv run python tests/test_real_upload.py
|
|
165
|
+
|
|
166
|
+
# 代码检查
|
|
167
|
+
uv run ruff check src
|
|
168
|
+
uv run ruff format src
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT
|