我有一个使用Python / Bottle编写的REST前端来处理文件上传,通常是大文件上传。 API的形式是:
客户端将PUT作为有效负载发送给文件。除其他外,它发送日期和授权标头。这是针对重播攻击的安全措施 - 请求使用临时密钥,使用目标网址,日期和其他一些内容进行烧录
现在问题。如果提供的日期在15分钟的给定日期时间窗口中,则服务器接受请求。如果上传需要足够长的时间,则会比允许的时间增量更长。现在,使用装饰器瓶视图方法完成请求授权处理。但是,除非上传完成,否则瓶子不会启动调度过程,因此在较长的上传时验证失败。
我的问题是:有没有办法解释瓶子或WSGI立即处理请求并流式传输上传?由于其他原因,这对我也很有用。或任何其他解决方案?在我写这篇文章时,我想到了WSGI中间件,但我仍然喜欢外部洞察力。
我愿意切换到Flask,甚至其他Python框架,因为REST前端非常轻量级。
谢谢
答案 0 :(得分:18)
我建议将传入的文件拆分为前端的较小大小的块。我这样做是为了在Flask应用程序中实现大文件上传的暂停/恢复功能。
使用Sebastian Tschan's jquery plugin,您可以在初始化插件时指定maxChunkSize
来实现分块,如:
$('#file-select').fileupload({
url: '/uploads/',
sequentialUploads: true,
done: function (e, data) {
console.log("uploaded: " + data.files[0].name)
},
maxChunkSize: 1000000 // 1 MB
});
现在,客户端在上传大文件时会发送多个请求。并且您的服务器端代码可以使用Content-Range
标头将原始大文件一起修补。对于Flask应用程序,视图可能类似于:
# Upload files
@app.route('/uploads/', methods=['POST'])
def results():
files = request.files
# assuming only one file is passed in the request
key = files.keys()[0]
value = files[key] # this is a Werkzeug FileStorage object
filename = value.filename
if 'Content-Range' in request.headers:
# extract starting byte from Content-Range header string
range_str = request.headers['Content-Range']
start_bytes = int(range_str.split(' ')[1].split('-')[0])
# append chunk to the file on disk, or create new
with open(filename, 'a') as f:
f.seek(start_bytes)
f.write(value.stream.read())
else:
# this is not a chunked request, so just save the whole file
value.save(filename)
# send response with appropriate mime type header
return jsonify({"name": value.filename,
"size": os.path.getsize(filename),
"url": 'uploads/' + value.filename,
"thumbnail_url": None,
"delete_url": None,
"delete_type": None,})
对于您的特定应用程序,您只需确保每个请求仍然发送正确的身份验证标头。
希望这有帮助!我有一段时间一直在努力解决这个问题;)
答案 1 :(得分:1)
使用plupload解决方案时可能就像这样:
$("#uploader").plupload({
// General settings
runtimes : 'html5,flash,silverlight,html4',
url : "/uploads/",
// Maximum file size
max_file_size : '20mb',
chunk_size: '128kb',
// Specify what files to browse for
filters : [
{title : "Image files", extensions : "jpg,gif,png"},
],
// Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that)
dragdrop: true,
// Views to activate
views: {
list: true,
thumbs: true, // Show thumbs
active: 'thumbs'
},
// Flash settings
flash_swf_url : '/static/js/plupload-2.1.2/js/plupload/js/Moxie.swf',
// Silverlight settings
silverlight_xap_url : '/static/js/plupload-2.1.2/js/plupload/js/Moxie.xap'
});
在这种情况下你的flask-python代码与此类似:
from werkzeug import secure_filename
# Upload files
@app.route('/uploads/', methods=['POST'])
def results():
content = request.files['file'].read()
filename = secure_filename(request.values['name'])
with open(filename, 'ab+') as fp:
fp.write(content)
# send response with appropriate mime type header
return jsonify({
"name": filename,
"size": os.path.getsize(filename),
"url": 'uploads/' + filename,})
Plupload总是以完全相同的顺序发送块,从头到尾,所以你不必费心去寻找或类似的东西。