我有一个使用stream_request_body
上传文件到服务器的tornado
应用程序。文件选择是一个HTML表单,其中JSonsubmit
函数用于执行上传处理程序。JS函数为async
+await fetch
。如果用户选择一个超过最大允许大小的文件,那么我在def prepare(self)
中使用self.set_status(400)
。在这种情况下,我还想发送/写一个文本字符串(self.write('File too big')
?),应该显示在文档中的元素作为信息给用户,我该怎么做?
使用我当前的JS脚本,我在浏览器控制台得到一个错误:
Promise { <state>: "pending" }
TypeError: Response.json: Body has already been consumed.
我对tornado
服务器设置的另一个问题是,即使我在def prepare(self)
函数中有return
,当文件大于最大允许时,然后执行def data_received
和def post
(文件实际上上传到服务器),为什么会这样?
感谢任何帮助/提示。我是新的tornado
和JS,所以抱歉,如果问题是非常基本的。
使用tornado ver 6.1, python 3.9
application.py
from tornado import version as tornado_version
from tornado.ioloop import IOLoop
import tornado.web
import uuid
import os
import json
MB = 1024 * 1024
GB = 1024 * MB
MAX_STREAMED_SIZE = 1024 #20 * GB
@tornado.web.stream_request_body
class UploadHandler(tornado.web.RequestHandler):
def initialize(self):
self.bytes_read = 0
self.loaded = 0
self.data = b''
def prepare(self):
self.content_len = int(self.request.headers.get('Content-Length'))
if self.content_len > MAX_STREAMED_SIZE:
txt = "Too big file"
print(txt)
self.set_status(400)
# how do I pass this txt to an document element?
self.write(json.dumps({'error': txt}))
# eventhough I have a return here execution is continued
# in data_received() and post() functions
# Why is that?
return
def data_received(self, chunk):
self.bytes_read += len(chunk)
self.data += chunk
def post(self):
value = self.data
fname = str(uuid.uuid4())
with open(fname, 'wb') as f:
f.write(value)
data = {'filename': fname}
print(json.dumps(data))
self.write(json.dumps(data))
class IndexHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def main():
handlers = [(r'/', IndexHandler), (r'/upload', UploadHandler)]
settings = dict(debug=True, template_path=os.path.dirname(__file__))
app = tornado.web.Application(handlers, **settings)
print(app)
app.listen(9999, address='localhost')
IOLoop().current().start()
if __name__ == '__main__':
print('Listening on localhost:9999')
print('Tornado ver:', tornado_version)
main()
index . html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Upload something!</title>
</head>
<body>
<h1>Upload</h1>
<form id="uploadForm">
<input type="file" name="file" id="file" />
<br />
<input type="submit" value="Upload">
</form>
<p><span id='display'></span></p>
<script>
uploadForm.onsubmit = async (e) => {
e.preventDefault();
var fileInput = document.getElementById('file');
var fileAttr = fileInput.files[0];
console.log(fileAttr);
var filename = fileInput.files[0].name;
console.log(filename);
document.getElementById('display').innerHTML =
'Uploading ' + document.getElementById("file").value;
let formData = new FormData(document.getElementById('uploadForm'));
try {
let response = await fetch(`${window.origin}/upload`, {
method: "POST",
body: formData,
});
if (!response.ok) {
console.log('error')
console.log(response.json());
// how do I update document.getElementById('display').innerHTML
// with tornado self.write when error response?
}
let result = await response.json();
console.log(result);
document.getElementById('display').innerHTML = 'Finished';
} catch(exception) {
console.log(exception);
}
};
</script>
</body>
</html>
在prepare
中,返回是不够的,您需要引发异常来停止处理。
你有两个选择:
-
使用提供的功能:在
RequestHandler
上覆盖write_error
以创建自定义错误响应,然后在print
之后的prepare
中覆盖raise tornado.web.HTTPError(400)
[1] -
自己动手:使用
self.set_status
设置错误状态码,self.write
当场写出你需要的任何东西,然后raise tornado.web.Finish
短路请求的处理。
使用您的代码,您基本上只需要将prepare
中的return
替换为raise tornado.web.Finish()
。显然,如果你要在多个地方执行此操作,那么使用#1是有意义的,但如果你只有现在的脚本,#2就可以了。