-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathupload_and_save.py
More file actions
84 lines (63 loc) · 2.64 KB
/
upload_and_save.py
File metadata and controls
84 lines (63 loc) · 2.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# Author: Anggit Arfanto
# Description: Upload, stream multipart/form-data and save.
import os
from urllib.parse import quote
from tremolo import Application
app = Application()
@app.route('/')
async def index():
return (
'<form action="/upload" method="post" enctype="multipart/form-data">'
'<p><input type="file" name="file1" /></p>'
'<p><input type="file" name="file2" /></p>'
'<p><input type="submit" value="Upload (Max. 100MiB)" /></p>'
'</form>'
)
@app.route('/upload')
async def upload(request, response):
# no worries, if the file is larger than `max_file_size`
# it can still be read continuously bit by bit according to this size
files = request.files(max_file_size=16384) # 16KiB
# keep track of incomplete writings
incomplete = set()
try:
# read while writing the file(s).
# `part` represents a field/file received in a multipart request
async for part in files:
filename = quote(part.get('filename', ''))
if not filename:
continue
with open('Uploaded_' + filename, 'wb') as fp:
incomplete.add(fp)
# stream a (possibly) large part in chunks
async for data in part.stream():
print('Writing %s (len=%d, eof=%s)' % (filename,
len(data),
part['eof']))
fp.write(data)
incomplete.discard(fp) # completed :)
filename = filename.encode()
content_type = quote(part['type']).encode()
yield (
b'File <a href="/download?type=%s&filename=%s">%s</a> '
b'was uploaded.<br />' % (content_type, filename, filename)
)
finally:
while incomplete:
path = incomplete.pop().name
print('Upload canceled, removing incomplete file: %s' % path)
os.unlink(path)
yield b''
@app.route('/download')
async def download(request, response):
# prepend / append a hardcoded string.
# do not let the user freely determine the path
path = 'Uploaded_' + quote(request.query['filename'][0])
content_type = request.query['type'][0]
await response.sendfile(path, content_type=content_type)
if __name__ == '__main__':
# 100MiB is a nice limit due to the default `app_handler_timeout=120`
# (120 seconds). however, it's perfectly fine to increase those limits
app.run('0.0.0.0', 8000, client_max_body_size=100 * 1048576)