tornado上传大文件以及多文件上传

tornado上传大文件问题解决方法

tornado默认上传限制为低于100M,但是由于需要上传大文件需求,网上很多说是用nginx,但我懒,同时不想在搞一个服务了。

解决方法:

    server = HTTPServer(application, max_buffer_size=504857600, max_body_size=504857600)
    server.bind(options.port)
    server.start(5)  # Forks multiple sub-process
    tornado.ioloop.IOLoop.current().start()

具体再去看源码,stackoverflow和/segmentfault.com给了我提示,然后跑去看了眼源码

TCPserver

 

.....

 

From上传文件

#!/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")[0].files[0];
            var fileObj2 = $("#img2")[0].files[0];
            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="50" 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")[0].files;
            var form = new FormData();//创建一个form对象类似<form>
            for(var i=0;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>
View Code

 

下载 ===注意: 中文名字会出现错误,需要重命名

#     @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"),
        }
View Code

 

posted @ 2016-07-27 18:31  我当道士那儿些年  阅读(5248)  评论(0编辑  收藏  举报