tornado上传大文件问题解决方法
tornado默认上传限制为低于100M,但是由于需要上传大文件需求,网上很多说是用nginx,但我懒,同时不想在搞一个服务了。
解决方法:
server = HTTPServer(application, max_buffer_size=, max_body_size=)
server.bind(options.port)
server.start() # Forks multiple sub-process
tornado.ioloop.IOLoop.current().start()
具体再去看源码,*和/segmentfault.com给了我提示,然后跑去看了眼源码
TCPserver
.....
From上传文件
aaarticlea/png;base64,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" alt="" />
#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
import os
file_name = os.path.join("img", file_name)
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'template',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
star.py
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>上传文件</title>
</head>
<body>
<!--表单中enctype="multipart/form-data"的意思,是设置表单的MIME编码。-->
<!--默认情况,这个编码格式是application/x-www-form-urlencoded,不能用于文件上传;-->
<!--只有使用了multipart/form-data,才能完整的传递文件数据,进行下面的操作. -->
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<input name="fff" id="my_file" type="file" />
<input type="submit" value="提交" />
</form>
</body>
</html>
index.html
AJAX上传
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = document.getElementById("img").files[0]; var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>
</html>
HTML - XMLHttpRequest
dataType:"json", -----主意加上这句
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script src="/static/jquery-1.12.4.js"></script>
<!--<script src="{{static_url('jquery-1.9.1.min.js')}}"></script> 也可以这么写-->
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();//创建一个form对象类似<form>
form.append("k1", "v1");
form.append("fff", fileObj); $.ajax({
type:'POST',
url: '/index',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
//如果不写这两句,改变里面的格式,影响内容
success: function(arg){
console.log(arg);
}
})
}
</script>
</body>
</html>
HTML - jQuery
思考一个问题:
如果我要是有两个上传的框呢?怎么办
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/admin/js/jquery-1.10.2.js"></script>
</head>
<body> <input type="file" id="img1"/>
<input type="file" id="img2"/>
<input type="button" onclick="UploadFile();" value="上传"/> <script type="text/javascript"> function UploadFile(){
var fileObj1 = $("#img1")[].files[];
var fileObj2 = $("#img2")[].files[];
var form = new FormData();//创建一个form对象类似<form>
form.append("k1", "v1");
form.append("file1", fileObj1);
form.append("file2", fileObj2); $.ajax({
type:'POST',
url: '/TestUploadFile',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
//如果不写这两句,改变里面的格式,影响内容
success: function(arg){
console.log(arg);
}
})
} </script>
</body>
</html>
两个上传文件input框
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<div id="main">
<input name="fff" id="my_file" type="file" />
<input type="button" name="action" value="Upload" onclick="redirect()"/>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
</div>
</form> <script>
function redirect(){
document.getElementById('my_iframe').onload = Testt;
document.getElementById('my_form').target = 'my_iframe';
document.getElementById('my_form').submit(); } function Testt(ths){
var t = $("#my_iframe").contents().find("body").text();
console.log(t);
}
</script>
</body>
</html>
HTML - iframe
#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'template',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
python
补充:如果python后台想得到aaa的值,需要用self.argument('aaa'),免得自己忘记了。感谢二卷!
'static_path': 'static',
<script type="text/javascript"> $(document).ready(function () { $("#formsubmit").click(function () { var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>'); $("body").append(iframe); var form = $('#theuploadform');
form.attr("action", "/upload.aspx");
form.attr("method", "post"); form.attr("encoding", "multipart/form-data");
form.attr("enctype", "multipart/form-data"); form.attr("target", "postiframe");
form.attr("file", $('#userfile').val());
form.submit(); $("#postiframe").load(function () {
iframeContents = this.contentWindow.document.body.innerHTML;
$("#textarea").html(iframeContents);
}); return false; }); }); </script> <form id="theuploadform">
<input id="userfile" name="userfile" size="" type="file" />
<input id="formsubmit" type="submit" value="Send File" />
</form> <div id="textarea">
</div>
扩展:基于iframe实现Ajax上传示例
tornado多文件上传js代码
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="file" multiple="multiple"/>
<input type="button" value="提交" onclick="UploadFile();" />
<script src="/static/jquery-1.12.4.js"></script>
<script>
function UploadFile(){
var fileObj = $("#file")[].files;
var form = new FormData();//创建一个form对象类似<form>
for(var i=;i<fileObj.length;i++){
form.append("file", fileObj[i]);
}
// var fileObj1 = $("#file")[0].files[1];
// var form = new FormData();//创建一个form对象类似<form>
// form.append("file", fileObj);
// form.append("file", fileObj1); $.ajax({
type:'POST',
url: '/index',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
//如果不写这两句,改变里面的格式,影响内容
success: function(arg){
console.log(arg); }
})
}
</script> </body>
</html>
下载 ===注意: 中文名字会出现错误,需要重命名
# @authenticated
# def get(self):
# print("2222222222222")
# locale = self.get_cookie("user_locale","zh_CN")
# msg = lang_upgrade[locale]
# back_filename_path = os.path.join(TEP_PATH,backup_filename)
# print('back_filename_path = ',back_filename_path)
# if os.path.exists(back_filename_path) and os.path.isfile(back_filename_path):
# self.set_header("Content-Type", "application/x-zip-compressed")
# self.set_header("Content-Disposition", "attachment; filename=%s" % backup_filename)
# f = open(back_filename_path,'rb')
# while True:
# b = f.read(8096)
# if not b:
# break
# else:
# self.write(b)
# self.flush()
# f.close()
# else:
# self.write(msg[4010])
前端对应代码:
window.location.href=DownFileAPI + "?UserID=" + userId + "&&TokenID=" + encodeURIComponent(tokenId) + "&&FilePathName=" + encodeURIComponent(dataVa)
下载报错信息:latin-1 codec cant encode characters in position 42-48: ordinal not in range256
今天遇到报错: latin-1 codec cant encode characters in position 42-48: ordinal not in range256
原因: 因为文件名是中文, header无法解析, 所以报错。
那这个时候怎么办呢?
解决办法:
FilePathName = self.get_argument("FilePathName", None) #获取文件名
FileName = str(FilePathName.split("/")[-1]).strip() # 得到文件名
latinFileName = FileName.encode("utf-8").decode("latin1") # 这句很关键, 要解析成latin-1才OK,这样就不会报错
示例:
class DownFileHandler(downBaseRequestHandler):
@tornado.gen.coroutine
def get(self, *args, **kwargs): if self.verifyFlag == 1 and self.status == 0:
status = 2000 try:
FilePathName = self.get_argument("FilePathName", None)
FilePathName = MyBase64.decryption(unquote(FilePathName)) # 这句是解密
FileName = str(FilePathName.split("/")[-1]).strip()
latinFileName = FileName.encode("utf-8").decode("latin1") # 这句很关键, 要解析成latin-1才OK,这样就不会报错
newFileName = str(int(time.time())) + "." + FileName.split(".")[1] # 第二种方式就是换一个文件名
self.set_header("Content-Type", "application/x-zip-compressed")
# -----<或者这么写> ----- #
# self.set_header("Content-Type", "application/octet-stream")
self.set_header("Content-Disposition", "attachment; filename=%s" % latinFileName)
f = open(FilePathName, 'rb')
while True:
b = f.read(8096)
if not b:
break
else:
self.write(b)
self.flush()
f.close() except Exception as e:
e = FormatErrorCode(e)
my_log.error(e)
status = int(e[0])
self.write(json.dumps(result(status=status)))
self.finish()
else:
self.write(json.dumps(result(status=4005)))
self.finish()
上传文件写入到文件夹并返回文件信息封装类
import hashlib class UploadFile:
'''
USE METHOD:
from app import static_path
# static_path==filePath--> '/opt/code/...' article_little_img = self.request.files["article_little_img"][0]
UploadFile(article_little_img, static_path)
''' def __init__(self, fileobj, static_path):
''' :param fileobj:
:param static_path:
'''
self.fileobj = fileobj
self.static_path = static_path
self.md5 = None
self.size = None
self.saveFile(self.fileobj) def getFullName(self, fileobj):
return fileobj['filename'] def getFilePath(self):
# 获取文件完整路径
rootPath = self.static_path # app里面总路径
filePath = datetime.datetime.now().strftime("%Y%m%d")
# for path in []:
# filePath = os.path.join(filePath, path)
return os.path.join(rootPath, 'admin', 'upload', 'img', filePath) def saveFile(self, fileobj):
self.fullName = self.getFullName(fileobj)
self.filePath = self.getFilePath() # 检查路径是否存在,不存在则创建
if not os.path.exists(self.filePath):
os.makedirs(self.filePath)
self.filePathName = os.path.join(self.filePath, self.fullName)
md5_value = hashlib.md5()
with open(self.filePathName, 'wb') as fp:
fp.write(fileobj['body'])
md5_value.update(fileobj['body'])
self.md5 = md5_value.hexdigest()
self.size = len(fileobj['body']) def getFileInfo(self):
''' :return:
'''
# 获取当前上传成功文件的各项信息
'''
# 注意替换 static路径 --> 用总路径替换static_path 得到后面路径,然后. http://192.168.2.137:8888/ + sub_filepathname
self.filePathName = '/opt/code/zzz/static/upload/img/123/123.png'
static_path = '/opt/code/zzz/'
url = http://192.168.2.137:8888/ + sub_filepathname
'''
return {
'state': 1,
'url': '',
'title': '',
'type': self.fullName.split('.')[0],
'size': self.size,
'md5': self.md5,
'fileName': self.fullName,
'filePath': self.filePath,
'filePathName': self.filePathName,
'fileDate': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}