fix: webserver: file uploading
- Fix upload of files <= Request.max_body_length File uploads smaller than the limit were not given to the handler as a stream by microdot, instead the content is directly stored in the request.body. - Refactor stream to file copy into a helper method - Increase the copy buffer size to 16k - Call app.reset_idle_timeout() periodically during file uploads to avoid the device turning off when uploading large files while on battery. Signed-off-by: Matthias Blankertz <matthias@blankertz.org>
This commit is contained in:
@@ -5,6 +5,7 @@ Copyright (c) 2024-2025 Stefan Kratochwil <Kratochwil-LA@gmx.de>
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import board
|
import board
|
||||||
|
import errno
|
||||||
import hwconfig
|
import hwconfig
|
||||||
import json
|
import json
|
||||||
import machine
|
import machine
|
||||||
@@ -202,6 +203,25 @@ async def audiofiles_get(request):
|
|||||||
return directory_iterator(), {'Content-Type': 'application/json; charset=UTF-8'}
|
return directory_iterator(), {'Content-Type': 'application/json; charset=UTF-8'}
|
||||||
|
|
||||||
|
|
||||||
|
async def stream_to_file(stream, file_, length):
|
||||||
|
data = array('b', range(16384))
|
||||||
|
bytes_copied = 0
|
||||||
|
while True:
|
||||||
|
bytes_read = await stream.readinto(data)
|
||||||
|
if bytes_read == 0:
|
||||||
|
# End of body
|
||||||
|
break
|
||||||
|
bytes_written = file_.write(data[:bytes_read])
|
||||||
|
if bytes_written != bytes_read:
|
||||||
|
# short writes shouldn't happen
|
||||||
|
raise OSError(errno.EIO, 'unexpected short write')
|
||||||
|
bytes_copied += bytes_written
|
||||||
|
if bytes_copied == length:
|
||||||
|
break
|
||||||
|
app.reset_idle_timeout()
|
||||||
|
return bytes_copied
|
||||||
|
|
||||||
|
|
||||||
@webapp.route('/api/v1/audiofiles', methods=['POST'])
|
@webapp.route('/api/v1/audiofiles', methods=['POST'])
|
||||||
async def audiofile_upload(request):
|
async def audiofile_upload(request):
|
||||||
if 'type' not in request.args or request.args['type'] not in ['file', 'directory']:
|
if 'type' not in request.args or request.args['type'] not in ['file', 'directory']:
|
||||||
@@ -218,26 +238,13 @@ async def audiofile_upload(request):
|
|||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
return '', 204
|
return '', 204
|
||||||
with open(path, 'wb') as newfile:
|
with open(path, 'wb') as newfile:
|
||||||
data = array('b', range(4096))
|
try:
|
||||||
bytes_copied = 0
|
if length > Request.max_body_length:
|
||||||
while True:
|
bytes_copied = await stream_to_file(request.stream, newfile, length)
|
||||||
try:
|
else:
|
||||||
bytes_read = await request.stream.readinto(data)
|
bytes_copied = newfile.write(request.body)
|
||||||
except OSError as ex:
|
except OSError as ex:
|
||||||
return f'read error: {ex}', 500
|
return f'error writing data to file: {ex}', 500
|
||||||
if bytes_read == 0:
|
|
||||||
# End of body
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
bytes_written = newfile.write(data[:bytes_read])
|
|
||||||
except OSError as ex:
|
|
||||||
return f'write error: {ex}', 500
|
|
||||||
if bytes_written != bytes_read:
|
|
||||||
# short writes shouldn't happen
|
|
||||||
return 'write failure', 500
|
|
||||||
bytes_copied += bytes_written
|
|
||||||
if bytes_copied == length:
|
|
||||||
break
|
|
||||||
if bytes_copied == length:
|
if bytes_copied == length:
|
||||||
return '', 204
|
return '', 204
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user