|
1 | 1 | aiohttp-s3-client |
2 | 2 | ================ |
3 | 3 |
|
4 | | -[](https://pypi.org/project/aiohttp-s3-client) [](https://pypi.org/project/aiohttp-s3-client) []() [](https://pypi.org/project/aiohttp-s3-client) [](https://pypi.org/project/aiohttp-s3-client) [](https://coveralls.io/github/mosquito/aiohttp-s3-client?branch=master)  |
| 4 | +[](https://pypi.org/project/aiohttp-s3-client) [](https://pypi.org/project/aiohttp-s3-client) []() [](https://pypi.org/project/aiohttp-s3-client) [](https://pypi.org/project/aiohttp-s3-client) [](https://coveralls.io/github/mosquito/aiohttp-s3-client?branch=master) [](https://github.com/aiokitchen/aiohttp-s3-client/actions/workflows/tests.yml) |
5 | 5 |
|
6 | 6 | The simple module for putting and getting object from Amazon S3 compatible endpoints |
7 | 7 |
|
@@ -296,3 +296,74 @@ await client.get_file_parallel( |
296 | 296 | workers_count=8, |
297 | 297 | ) |
298 | 298 | ``` |
| 299 | + |
| 300 | +### Manual multipart upload |
| 301 | + |
| 302 | +You can also manually control multipart upload process using `multipart_upload` method. |
| 303 | +It returns an async context manager which handles upload creation and completion. |
| 304 | +This method gives you more control over the upload process, for example you can |
| 305 | +specify part size, add custom metadata, or control concurrency. |
| 306 | + |
| 307 | +#### Important multipart restrictions and recommendations: |
| 308 | + |
| 309 | +- **Minimum part size: 5 MiB** (`5 * 1024 * 1024` bytes). Every part must be at least |
| 310 | + 5 MiB in size, except for the final part. |
| 311 | +- **Maximum number of parts: `10,000`.** The total number of uploaded parts must be |
| 312 | + <= `10,000`. |
| 313 | +- Choosing a part size: **pick a part size that satisfies both constraints**. |
| 314 | + A safe formula when you know the total object size is: |
| 315 | + ```python |
| 316 | + part_size = max(5 * 1024 * 1024, math.ceil(total_size / 10000)) |
| 317 | + ``` |
| 318 | +- If you don't know the total size in advance, choose a conservative part size |
| 319 | + (for example 8 MiB or 16 MiB) so you are unlikely to exceed 10,000 parts. |
| 320 | +- The uploader implements retries for failed part uploads; you should still |
| 321 | + ensure parts (except the last) meet the 5 MiB minimum before uploading. |
| 322 | + |
| 323 | +The `put_part` method returns a coroutine — calling `put_part(...)` does not |
| 324 | +perform the network upload immediately, it registers the part (and its part |
| 325 | +number) and returns a coroutine which performs the actual upload when awaited. |
| 326 | +This lets you schedule uploads and then await them concurrently. |
| 327 | + |
| 328 | +Note: the coroutine returned by `put_part(...)` performs the actual network |
| 329 | +upload when awaited and the uploader will automatically retry failed part |
| 330 | +uploads according to its retry policy; awaiting the coroutine will run those |
| 331 | +retries for that part. You don't need to retry manually when using the |
| 332 | +returned coroutine — the uploader handles integrity checks and retrying. |
| 333 | + |
| 334 | +#### Important usage notes: |
| 335 | + |
| 336 | +- You **MUST** call `put_part(...)` in the logical part sequence so parts get the |
| 337 | + correct part numbers (the uploader assigns part numbers in call order). |
| 338 | +- You **MAY** await the returned coroutines later and in any concurrency pattern you |
| 339 | + like (for example with `asyncio.gather`), which enables concurrent part |
| 340 | + uploads. |
| 341 | + |
| 342 | +#### Examples |
| 343 | + |
| 344 | +Create parts then upload them concurrently: |
| 345 | + |
| 346 | +```python |
| 347 | +import hashlib |
| 348 | +import aiohttp |
| 349 | +from aiohttp_s3_client import S3Client |
| 350 | + |
| 351 | +client = S3Client(url="http://your-s3-host", session=aiohttp.ClientSession()) |
| 352 | + |
| 353 | +async with client.multipart_upload("test/video.mov") as uploader: |
| 354 | + uploads = [] |
| 355 | + |
| 356 | + # Call put_part in the correct part sequence and collect coroutines. |
| 357 | + # The uploader assigns part numbers in the order put_part is called. |
| 358 | + for chunk in chunks: |
| 359 | + uploads.append( |
| 360 | + uploader.put_part( |
| 361 | + chunk, |
| 362 | + content_sha256=hashlib.sha256(chunk).hexdigest(), |
| 363 | + ) |
| 364 | + ) |
| 365 | + |
| 366 | + # Now execute all part uploads concurrently. The uploader will handle |
| 367 | + # retries and integrity checks for each part. |
| 368 | + await asyncio.gather(*uploads) |
| 369 | +``` |
0 commit comments