resumable-upload 0.0.1__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.
- resumable_upload-0.0.1/CHANGELOG.md +41 -0
- resumable_upload-0.0.1/LICENSE +21 -0
- resumable_upload-0.0.1/MANIFEST.in +9 -0
- resumable_upload-0.0.1/PKG-INFO +424 -0
- resumable_upload-0.0.1/README.ko.md +380 -0
- resumable_upload-0.0.1/README.md +380 -0
- resumable_upload-0.0.1/TUS_COMPLIANCE.md +164 -0
- resumable_upload-0.0.1/examples/advanced_client_example.py +377 -0
- resumable_upload-0.0.1/examples/client_example.py +54 -0
- resumable_upload-0.0.1/examples/django_example.py +113 -0
- resumable_upload-0.0.1/examples/fastapi_example.py +64 -0
- resumable_upload-0.0.1/examples/flask_example.py +53 -0
- resumable_upload-0.0.1/examples/server_example.py +47 -0
- resumable_upload-0.0.1/pyproject.toml +102 -0
- resumable_upload-0.0.1/resumable_upload/__init__.py +29 -0
- resumable_upload-0.0.1/resumable_upload/client/__init__.py +7 -0
- resumable_upload-0.0.1/resumable_upload/client/base.py +379 -0
- resumable_upload-0.0.1/resumable_upload/client/retry.py +203 -0
- resumable_upload-0.0.1/resumable_upload/client/stats.py +66 -0
- resumable_upload-0.0.1/resumable_upload/exceptions.py +30 -0
- resumable_upload-0.0.1/resumable_upload/fingerprint.py +60 -0
- resumable_upload-0.0.1/resumable_upload/server.py +347 -0
- resumable_upload-0.0.1/resumable_upload/storage.py +170 -0
- resumable_upload-0.0.1/resumable_upload/url_storage.py +104 -0
- resumable_upload-0.0.1/resumable_upload.egg-info/PKG-INFO +424 -0
- resumable_upload-0.0.1/resumable_upload.egg-info/SOURCES.txt +28 -0
- resumable_upload-0.0.1/resumable_upload.egg-info/dependency_links.txt +1 -0
- resumable_upload-0.0.1/resumable_upload.egg-info/requires.txt +16 -0
- resumable_upload-0.0.1/resumable_upload.egg-info/top_level.txt +1 -0
- resumable_upload-0.0.1/setup.cfg +4 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.0.1] - 2025-01-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial release of resumable-upload library
|
|
13
|
+
- TUS protocol v1.0.0 server implementation
|
|
14
|
+
- TUS protocol v1.0.0 client implementation
|
|
15
|
+
- TusClientWithRetry with automatic retry and exponential backoff
|
|
16
|
+
- SQLiteStorage backend for upload state management
|
|
17
|
+
- Comprehensive logging support (INFO, WARNING, ERROR, DEBUG levels)
|
|
18
|
+
- Custom exceptions: TusCommunicationError and TusUploadFailed
|
|
19
|
+
- File fingerprinting for cross-session resumability
|
|
20
|
+
- URL storage interface with FileURLStorage implementation
|
|
21
|
+
- Progress tracking with UploadStats dataclass
|
|
22
|
+
- Support for file streams alongside file paths
|
|
23
|
+
- Optional SHA1 checksum verification
|
|
24
|
+
- TLS certificate verification control
|
|
25
|
+
- Integration examples for Flask, FastAPI, and Django
|
|
26
|
+
- 70 comprehensive tests with 90%+ coverage
|
|
27
|
+
- Support for Python 3.9 through 3.14
|
|
28
|
+
- Zero runtime dependencies
|
|
29
|
+
- Complete documentation in English and Korean
|
|
30
|
+
- TUS protocol compliance documentation
|
|
31
|
+
|
|
32
|
+
### Features
|
|
33
|
+
|
|
34
|
+
- Sequential chunk uploads per TUS protocol requirements
|
|
35
|
+
- Automatic resume of interrupted uploads
|
|
36
|
+
- Configurable chunk size
|
|
37
|
+
- Metadata support with proper encoding
|
|
38
|
+
- Cross-platform compatibility
|
|
39
|
+
- PyPI-ready package structure
|
|
40
|
+
|
|
41
|
+
[0.0.1]: https://github.com/sts07142/resumable-upload/releases/tag/v0.0.1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Injae Ryou
|
|
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,424 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: resumable-upload
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A Python implementation of the `TUS resumable upload protocol` for server and client, with zero runtime dependencies.
|
|
5
|
+
Author-email: Injae Ryou <sts07142@naver.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sts07142/resumable-upload
|
|
8
|
+
Project-URL: Documentation, https://github.com/sts07142/resumable-upload#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/sts07142/resumable-upload
|
|
10
|
+
Project-URL: Issues, https://github.com/sts07142/resumable-upload/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/sts07142/resumable-upload/releases
|
|
12
|
+
Keywords: tus,upload,resumable,file-upload,server,client,http
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
17
|
+
Classifier: Topic :: System :: Networking
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
34
|
+
Requires-Dist: tox>=4.0.0; extra == "dev"
|
|
35
|
+
Provides-Extra: test
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
37
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
38
|
+
Requires-Dist: flask>=2.0.0; extra == "test"
|
|
39
|
+
Requires-Dist: fastapi>=0.100.0; extra == "test"
|
|
40
|
+
Requires-Dist: uvicorn>=0.38.0; extra == "test"
|
|
41
|
+
Requires-Dist: httpx>=0.24.0; extra == "test"
|
|
42
|
+
Requires-Dist: django>=3.2.0; extra == "test"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
# Resumable Upload
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/resumable-upload/)
|
|
48
|
+
[](https://pypi.org/project/resumable-upload/)
|
|
49
|
+
[](https://github.com/sts07142/resumable-upload/blob/main/LICENSE)
|
|
50
|
+
|
|
51
|
+
**English** | [ํ๊ตญ์ด](README.ko.md)
|
|
52
|
+
|
|
53
|
+
A Python implementation of the [TUS resumable upload protocol](https://tus.io/) v1.0.0 for server and client, with zero runtime dependencies.
|
|
54
|
+
|
|
55
|
+
## โจ Features
|
|
56
|
+
|
|
57
|
+
- ๐ **Zero Dependencies**: Built using Python standard library only (no external dependencies for core functionality)
|
|
58
|
+
- ๐ฆ **Server & Client**: Complete implementation of both sides
|
|
59
|
+
- ๐ **Resume Capability**: Automatically resume interrupted uploads
|
|
60
|
+
- โ
**Data Integrity**: Optional SHA1 checksum verification
|
|
61
|
+
- ๐ **Retry Logic**: Built-in automatic retry with exponential backoff
|
|
62
|
+
- ๐ **Progress Tracking**: Detailed upload progress callbacks with stats
|
|
63
|
+
- ๐ **Web Framework Support**: Integration examples for Flask, FastAPI, and Django
|
|
64
|
+
- ๐ **Python 3.9+**: Supports Python 3.9 through 3.14
|
|
65
|
+
- ๐ช **Storage Backend**: SQLite-based storage (extensible to other backends)
|
|
66
|
+
- ๐ **TLS Support**: Certificate verification control and mTLS authentication
|
|
67
|
+
- ๐ **URL Storage**: Persist upload URLs across sessions
|
|
68
|
+
- ๐ฏ **TUS Protocol Compliant**: Implements TUS v1.0.0 core protocol with creation, termination, and checksum extensions
|
|
69
|
+
|
|
70
|
+
## ๐ฆ Installation
|
|
71
|
+
|
|
72
|
+
### Using uv (Recommended)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Install uv if you haven't already
|
|
76
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
77
|
+
|
|
78
|
+
# Install the package
|
|
79
|
+
uv pip install resumable-upload
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Using pip
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install resumable-upload
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## ๐ Quick Start
|
|
89
|
+
|
|
90
|
+
### Basic Server
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from http.server import HTTPServer
|
|
94
|
+
from resumable_upload import TusServer, TusHTTPRequestHandler, SQLiteStorage
|
|
95
|
+
|
|
96
|
+
# Create storage backend
|
|
97
|
+
storage = SQLiteStorage(db_path="uploads.db", upload_dir="uploads")
|
|
98
|
+
|
|
99
|
+
# Create TUS server
|
|
100
|
+
tus_server = TusServer(storage=storage, base_path="/files")
|
|
101
|
+
|
|
102
|
+
# Create HTTP handler
|
|
103
|
+
class Handler(TusHTTPRequestHandler):
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
Handler.tus_server = tus_server
|
|
107
|
+
|
|
108
|
+
# Start server
|
|
109
|
+
server = HTTPServer(("0.0.0.0", 8080), Handler)
|
|
110
|
+
print("Server running on http://localhost:8080")
|
|
111
|
+
server.serve_forever()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Basic Client
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from resumable_upload import TusClient
|
|
118
|
+
|
|
119
|
+
# Create client
|
|
120
|
+
client = TusClient("http://localhost:8080/files")
|
|
121
|
+
|
|
122
|
+
# Upload file with progress callback
|
|
123
|
+
def progress(uploaded, total):
|
|
124
|
+
print(f"Progress: {uploaded}/{total} bytes ({uploaded/total*100:.1f}%)")
|
|
125
|
+
|
|
126
|
+
upload_url = client.upload_file(
|
|
127
|
+
"large_file.bin",
|
|
128
|
+
metadata={"filename": "large_file.bin"},
|
|
129
|
+
progress_callback=progress
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
print(f"Upload complete: {upload_url}")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## ๐ง Advanced Usage
|
|
136
|
+
|
|
137
|
+
### Client with Automatic Retry
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from resumable_upload import TusClientWithRetry
|
|
141
|
+
|
|
142
|
+
# Create client with retry capability
|
|
143
|
+
client = TusClientWithRetry(
|
|
144
|
+
"http://localhost:8080/files",
|
|
145
|
+
chunk_size=1.5*1024*1024, # 1.5MB chunks (float is allowed)
|
|
146
|
+
max_retries=3, # Retry up to 3 times
|
|
147
|
+
retry_delay=1.0, # Initial delay between retries
|
|
148
|
+
checksum=True # Enable checksum verification
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Upload with detailed progress tracking
|
|
152
|
+
def progress_callback(stats):
|
|
153
|
+
print(f"Progress: {stats.progress_percent:.1f}% | "
|
|
154
|
+
f"Speed: {stats.upload_speed/1024/1024:.2f} MB/s | "
|
|
155
|
+
f"ETA: {stats.eta_seconds:.0f}s | "
|
|
156
|
+
f"Chunks: {stats.chunks_completed}/{stats.total_chunks} | "
|
|
157
|
+
f"Retried: {stats.chunks_retried}")
|
|
158
|
+
|
|
159
|
+
upload_url = client.upload_file(
|
|
160
|
+
"large_file.bin",
|
|
161
|
+
metadata={"filename": "large_file.bin"},
|
|
162
|
+
progress_callback=progress_callback
|
|
163
|
+
)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Resume Interrupted Uploads
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
# Resume an interrupted upload
|
|
170
|
+
upload_url = client.resume_upload("large_file.bin", upload_url)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Cross-Session Resumability
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from resumable_upload import TusClient, FileURLStorage
|
|
177
|
+
|
|
178
|
+
# Enable URL storage for resumability across sessions
|
|
179
|
+
storage = FileURLStorage(".tus_urls.json")
|
|
180
|
+
client = TusClient(
|
|
181
|
+
"http://localhost:8080/files",
|
|
182
|
+
store_url=True,
|
|
183
|
+
url_storage=storage
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Upload will automatically resume if interrupted and restarted
|
|
187
|
+
upload_url = client.upload_file("large_file.bin")
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Using File Streams
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
# Upload from a file stream instead of a path
|
|
194
|
+
with open("file.bin", "rb") as fs:
|
|
195
|
+
client = TusClient("http://localhost:8080/files")
|
|
196
|
+
upload_url = client.upload_file(
|
|
197
|
+
file_stream=fs,
|
|
198
|
+
metadata={"filename": "file.bin"}
|
|
199
|
+
)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Exception Handling
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from resumable_upload import TusClient
|
|
206
|
+
from resumable_upload.exceptions import TusCommunicationError, TusUploadFailed
|
|
207
|
+
|
|
208
|
+
client = TusClient("http://localhost:8080/files")
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
upload_url = client.upload_file("file.bin")
|
|
212
|
+
except TusCommunicationError as e:
|
|
213
|
+
print(f"Communication error: {e.message}, status: {e.status_code}")
|
|
214
|
+
except TusUploadFailed as e:
|
|
215
|
+
print(f"Upload failed: {e.message}")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## ๐ Web Framework Integration
|
|
219
|
+
|
|
220
|
+
### Flask
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from flask import Flask, request, make_response
|
|
224
|
+
from resumable_upload import TusServer, SQLiteStorage
|
|
225
|
+
|
|
226
|
+
app = Flask(__name__)
|
|
227
|
+
tus_server = TusServer(storage=SQLiteStorage())
|
|
228
|
+
|
|
229
|
+
@app.route('/files', methods=['OPTIONS', 'POST'])
|
|
230
|
+
@app.route('/files/<upload_id>', methods=['HEAD', 'PATCH', 'DELETE'])
|
|
231
|
+
def handle_upload(upload_id=None):
|
|
232
|
+
status, headers, body = tus_server.handle_request(
|
|
233
|
+
request.method, request.path, dict(request.headers), request.get_data()
|
|
234
|
+
)
|
|
235
|
+
response = make_response(body, status)
|
|
236
|
+
for key, value in headers.items():
|
|
237
|
+
response.headers[key] = value
|
|
238
|
+
return response
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### FastAPI
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from fastapi import FastAPI, Request, Response
|
|
245
|
+
from resumable_upload import TusServer, SQLiteStorage
|
|
246
|
+
|
|
247
|
+
app = FastAPI()
|
|
248
|
+
tus_server = TusServer(storage=SQLiteStorage())
|
|
249
|
+
|
|
250
|
+
@app.post("/files")
|
|
251
|
+
@app.head("/files/{upload_id}")
|
|
252
|
+
@app.patch("/files/{upload_id}")
|
|
253
|
+
@app.delete("/files/{upload_id}")
|
|
254
|
+
async def handle_upload(request: Request):
|
|
255
|
+
body = await request.body()
|
|
256
|
+
status, headers, response_body = tus_server.handle_request(
|
|
257
|
+
request.method, request.url.path, dict(request.headers), body
|
|
258
|
+
)
|
|
259
|
+
return Response(content=response_body, status_code=status, headers=headers)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Django
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from django.http import HttpResponse
|
|
266
|
+
from django.views.decorators.csrf import csrf_exempt
|
|
267
|
+
from resumable_upload import TusServer, SQLiteStorage
|
|
268
|
+
|
|
269
|
+
tus_server = TusServer(storage=SQLiteStorage())
|
|
270
|
+
|
|
271
|
+
@csrf_exempt
|
|
272
|
+
def tus_upload_view(request, upload_id=None):
|
|
273
|
+
headers = {key[5:].replace('_', '-'): value
|
|
274
|
+
for key, value in request.META.items() if key.startswith('HTTP_')}
|
|
275
|
+
status, response_headers, response_body = tus_server.handle_request(
|
|
276
|
+
request.method, request.path, headers, request.body
|
|
277
|
+
)
|
|
278
|
+
response = HttpResponse(response_body, status=status)
|
|
279
|
+
for key, value in response_headers.items():
|
|
280
|
+
response[key] = value
|
|
281
|
+
return response
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## ๐ API Reference
|
|
285
|
+
|
|
286
|
+
### TusClient
|
|
287
|
+
|
|
288
|
+
Main client class for uploading files.
|
|
289
|
+
|
|
290
|
+
**Parameters:**
|
|
291
|
+
|
|
292
|
+
- `url` (str): TUS server base URL
|
|
293
|
+
- `chunk_size` (int): Size of each upload chunk in bytes (default: 1MB)
|
|
294
|
+
- `checksum` (bool): Enable SHA1 checksum verification (default: False)
|
|
295
|
+
- `store_url` (bool): Store upload URLs for resumability (default: False)
|
|
296
|
+
- `url_storage` (URLStorage): URL storage backend (default: FileURLStorage)
|
|
297
|
+
- `verify_tls_cert` (bool): Verify TLS certificates (default: True)
|
|
298
|
+
- `metadata_encoding` (str): Metadata encoding (default: "utf-8")
|
|
299
|
+
|
|
300
|
+
**Methods:**
|
|
301
|
+
|
|
302
|
+
- `upload_file(file_path=None, file_stream=None, metadata={}, progress_callback=None)`: Upload a file
|
|
303
|
+
- `resume_upload(file_path, upload_url, progress_callback=None)`: Resume an interrupted upload
|
|
304
|
+
- `delete_upload(upload_url)`: Delete an upload
|
|
305
|
+
- `get_offset(upload_url)`: Get current upload offset
|
|
306
|
+
|
|
307
|
+
### TusClientWithRetry
|
|
308
|
+
|
|
309
|
+
Enhanced client with automatic retry capability (inherits from TusClient).
|
|
310
|
+
|
|
311
|
+
**Additional Parameters:**
|
|
312
|
+
|
|
313
|
+
- `max_retries` (int): Maximum number of retry attempts (default: 3)
|
|
314
|
+
- `retry_delay` (float): Initial delay between retries in seconds (default: 1.0)
|
|
315
|
+
- `max_retry_delay` (float): Maximum delay between retries in seconds (default: 60.0)
|
|
316
|
+
|
|
317
|
+
### TusServer
|
|
318
|
+
|
|
319
|
+
Server implementation of TUS protocol.
|
|
320
|
+
|
|
321
|
+
**Parameters:**
|
|
322
|
+
|
|
323
|
+
- `storage` (Storage): Storage backend for managing uploads
|
|
324
|
+
- `base_path` (str): Base path for TUS endpoints (default: "/files")
|
|
325
|
+
- `max_size` (int): Maximum upload size in bytes (default: None)
|
|
326
|
+
|
|
327
|
+
**Methods:**
|
|
328
|
+
|
|
329
|
+
- `handle_request(method, path, headers, body)`: Handle TUS protocol requests
|
|
330
|
+
|
|
331
|
+
### SQLiteStorage
|
|
332
|
+
|
|
333
|
+
SQLite-based storage backend.
|
|
334
|
+
|
|
335
|
+
**Parameters:**
|
|
336
|
+
|
|
337
|
+
- `db_path` (str): Path to SQLite database file (default: "uploads.db")
|
|
338
|
+
- `upload_dir` (str): Directory for storing upload files (default: "uploads")
|
|
339
|
+
|
|
340
|
+
## ๐ TUS Protocol Compliance
|
|
341
|
+
|
|
342
|
+
This library implements TUS protocol version 1.0.0 with the following extensions:
|
|
343
|
+
|
|
344
|
+
- โ
**Core Protocol**: Basic upload functionality (POST, HEAD, PATCH)
|
|
345
|
+
- โ
**Creation**: Upload creation via POST
|
|
346
|
+
- โ
**Termination**: Upload deletion via DELETE
|
|
347
|
+
- โ
**Checksum**: SHA1 checksum verification
|
|
348
|
+
|
|
349
|
+
### Sequential Upload Requirement
|
|
350
|
+
|
|
351
|
+
**Important:** The TUS protocol requires chunks to be uploaded **sequentially**, not in parallel.
|
|
352
|
+
|
|
353
|
+
**Why Sequential?**
|
|
354
|
+
|
|
355
|
+
1. **Offset Validation**: Each chunk must be uploaded at the correct byte offset
|
|
356
|
+
2. **Data Integrity**: Prevents data corruption from race conditions
|
|
357
|
+
3. **Resume Capability**: Makes tracking received bytes straightforward and reliable
|
|
358
|
+
4. **Protocol Compliance**: TUS specification requires `Upload-Offset` to match current position
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
# โ Parallel uploads cause conflicts:
|
|
362
|
+
# Chunk 1 at offset 0 โ OK
|
|
363
|
+
# Chunk 3 at offset 2048 โ FAIL (409: expected offset 1024)
|
|
364
|
+
# Chunk 2 at offset 1024 โ FAIL (409: offset mismatch)
|
|
365
|
+
|
|
366
|
+
# โ
Sequential uploads work correctly:
|
|
367
|
+
# Chunk 1 at offset 0 โ OK (offset now 1024)
|
|
368
|
+
# Chunk 2 at offset 1024 โ OK (offset now 2048)
|
|
369
|
+
# Chunk 3 at offset 2048 โ OK (offset now 3072)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## ๐งช Testing
|
|
373
|
+
|
|
374
|
+
### Using uv (Recommended)
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# Install uv if you haven't already
|
|
378
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
379
|
+
|
|
380
|
+
# Create virtual environment and install dependencies
|
|
381
|
+
uv venv
|
|
382
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
383
|
+
|
|
384
|
+
# Install all dependencies (dev and test)
|
|
385
|
+
make install
|
|
386
|
+
|
|
387
|
+
# Run minimal tests (excluding web frameworks)
|
|
388
|
+
make test-minimal
|
|
389
|
+
|
|
390
|
+
# Run all tests (including web frameworks)
|
|
391
|
+
make test
|
|
392
|
+
|
|
393
|
+
# Or use Makefile for convenience
|
|
394
|
+
make lint # Run linting
|
|
395
|
+
make format # Format code
|
|
396
|
+
make test-minimal # Run minimal tests
|
|
397
|
+
make test # Run all tests
|
|
398
|
+
make test-all-versions # Test on all Python versions (3.9-3.14) - requires tox
|
|
399
|
+
make ci # Run full CI checks (lint + format + test)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
## ๐ Documentation
|
|
403
|
+
|
|
404
|
+
- **English**: [README.md](README.md)
|
|
405
|
+
- **ํ๊ตญ์ด (Korean)**: [README.ko.md](README.ko.md)
|
|
406
|
+
- **TUS Protocol Compliance**: [TUS_COMPLIANCE.md](TUS_COMPLIANCE.md)
|
|
407
|
+
|
|
408
|
+
## ๐ค Contributing
|
|
409
|
+
|
|
410
|
+
Contributions are welcome! Please check out the [Contributing Guide](.github/CONTRIBUTING.md) for guidelines.
|
|
411
|
+
|
|
412
|
+
## ๐ License
|
|
413
|
+
|
|
414
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
415
|
+
|
|
416
|
+
## ๐ Acknowledgments
|
|
417
|
+
|
|
418
|
+
This library is inspired by the official [TUS Python client](https://github.com/tus/tus-py-client) and implements the [TUS resumable upload protocol](https://tus.io/).
|
|
419
|
+
|
|
420
|
+
## ๐ Support
|
|
421
|
+
|
|
422
|
+
- ๐ซ Issues: [GitHub Issues](https://github.com/sts07142/resumable-upload/issues)
|
|
423
|
+
- ๐ Documentation: [GitHub README](https://github.com/sts07142/resumable-upload#readme)
|
|
424
|
+
- ๐ Star us on GitHub!
|