最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于tornado服务器实现文件上传和下载

    正文概述    2020-09-17   246

    基于tornado服务器实现文件上传和下载

    文件上传的服务端技术解析

    常言到,爱有多深、恨有多切。tornado服务器就是这样一个矛盾体,它的缺点和它的优点一样,显著且强烈。有人认为,文件上传是tornado的重大缺陷之一,它把用户上传的文件存放在内存中——这意味着多用户同时上传文件的话,内存的开销会急剧增加。不过我倒是觉得,这反倒让很多事情变得简单了,比如,你想对用户上传的内容做处理的话,不用再打开文件了,因为内容就在内存中。再者说,在tornado的异步机制下,我不确定真的能够多用户同时上传文件。这是一个有趣的问题。

    好了,言归正传吧。假定文件上传的表单如下:

    <form id="form_upload" action="/demo/upload" enctype="multipart/form-data" method="post">
        <input type="file" name="want_to_upload_file_1"/><br/>
        <input type="file" name="want_to_upload_file_2"/><br/>
        <input type="submit" value="上传"/>
    </form>

    机制是允许一次上传多个文件的。这里有几个问题需要特别说明一下。

    在提交表单之前,需要为form指定action和method的属性值,如果是上传文件,还要设置enctype=“multipart/form-data”。这三个属性,可以写在html中,也可以在submit之前用js的方法为其赋值。

    文件浏览是file类型的input标签自备的功能,程序员无法在浏览器框架内操作本地文件。该标签的name属性,是用来区别于其他文件的标识,不是文件名,也不是文件对象,更不是文件内容。

    上面的表单被提交到/demo/upload(假定上传的第1个文件名为dqd.jpg,第2个文件名为intro.png),这个请求的对象中包含files字典,上传的全部文件的信息都包含在这个结构中。我们来看看这个request.files的真实面貌:

    def post(self):
        print self.request.files.keys() # [u'want_to_upload_file_1', u'want_to_upload_file_2']
        print type(self.request.files['want_to_upload_file_1']) # list,长度为1
        meta_file_1 = self.request.files['want_to_upload_file_1'][0]
        print meta_file_1.keys() # ['body', 'content_type', 'filename']
        print len(meta_file_1['body']) # 31492,文件长度
        print meta_file_1['content_type'] # image/jpeg
        print meta_file_1['filename'] # dqd.jpg

    有了这些素材,我们就可以无所不能地应对客户需求了。比如,不做任何处理,仅仅用原文件名保存在指定路径下(假设保存在/static/image/wiki目录下):

    PROJECT_PATH = os.path.split(os.path.realpath(__file__))[0]
    upload_path = os.path.join(PROJECT_PATH, 'static', 'image', 'wiki')
    file_name = os.path.join(upload_path, meta_file_1['filename'])
    with open(file_name, 'wb') as fp:
        fp.write(meta_file_1['body'])

    很多时候,需要对用户上传的文件重命名(比如,用时间戳为文件名),但文件后缀名不变。

    fn, ext = os.path.splitext(meta_file_1['filename'])
    fn = '%d%s' % (time.time()*1000, ext)
    file_name = os.path.join(upload_path, fn)

    如果需要对用户上传的文件类型做检查,请使用文件的content_type,而不是文件的扩展名,因为前者更规范。比如,JPEG类型的图片文件,其后缀名可能是.jpg|.jpeg|.JPG|.JPEG中的一种,而前者只有“image/jpeg”一种表示法。

    关于文件的content_type,网上资料多如牛毛,请自行搜索。

    处理用户上传的图片文件时,除了限制文件大小,有时候还要做缩放处理,甚至一并生成缩略图,此时就需要将文件内容转成易于处理的图像对象,比如,pil的Image。

    from PIL import Image
    import StringIO
    pilImg = Image.open(StringIO.StringIO(meta_file_1['body']))
    print pilImg.size

    至于如何缩放、如何保存为文件,请自行检索相关资料。

    基于Ajax技术实现的文件上传客户端

    假定上传文件的表单是这样的:

    <form id="form_upload" action="/demo/upload" enctype="multipart/form-data" method="post">
        <input type="file" name="wiki_img" id="wiki_img" /><br />
        <input id="doUpload" type="button" value="上传" />
    </form>

    方法1:使用 ajaxfileupload.js

    <script src="jquery.js"></script>
    <script src="ajaxfileupload.js"></script>
    <script type="text/javascript">
        $("#doUpload").click(function(){
            $.ajaxFileUpload({
                url:'/demo/upload',
                secureuri:false,
                fileElementId:'wiki_img',
                dataType: 'json',
                success: function(data){
                    alert(data);
                }
            });
        });
    </script>

    方法2:仅依赖 jquery.js

    <script src="jquery.js"></script>
    <script type="text/javascript">
        var formData = new FormData();
        formData.append("file", $("#wiki_img")[0].files[0]);
        formData.append("filename", $("#wiki_img").val());
        
        $.ajax({
            url : '/demo/upload',
            type : 'POST',
            async : false,
            data : formData,
            processData : false,
            contentType : false,
            beforeSend:function(){
                $("#upload_tips").html("正在进行,请稍候");
            },
            success : function(data) {
                alert(data);
            }
        });
    </script>

    文件下载的服务端技术解析

    相对于上传,文件的下载就简单得多。只需要记住两点:开始前告诉浏览器要传输的文件类型,结束前对浏览器说拜拜。文件类型并不是固定的,需要根据文件的实际情况来选择。详情请自行检索。

    def get(self):
        self.set_header ('Content-Type', 'application/octet-stream')
        with open(filename, 'rb') as f:
            while True:
                data = f.read(buf_size)
                if not data:
                    break
                self.write(data)
        self.finish()

    配合seek命令,可以实现更复杂的下载请求,比如,断点续传、分块下载、ajax异步请求等。

    众多python培训视频,尽在python学习网,欢迎在线学习!


    起源地下载网 » 基于tornado服务器实现文件上传和下载

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元