上传文件

Posted 问君能有几多愁

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了上传文件相关的知识,希望对你有一定的参考价值。

上传文件

1、Form表单上传

接下来我们使用html标签来创建文件上传表单,以下为要注意的点:

  • form表单 method 属性必须设置为 POST 方法 ,不能使用 GET 方法。
  • form表单 enctype 属性需要设置为 multipart/form-data

  enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。默认地,表单数据会编码为 “application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值)。而当设置了该编码格式时,不能直接上传文件。因此,这里我们使用另外一种编码格式,即multipart/form-data,该编码格式不对数据进行编码,而是直接上传二进制数据,form里面的input的值以二进制的方式传过去。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>上传文件</title>
</head>
<body>
    <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
Python

注意:这种传统的表单上传,属于"同步上传"。也就是说,点击上传按钮后,网页"锁死",用户只能等待上传结束,然后浏览器刷新,跳到表单的action属性指定的网址。

2、AJAX上传

  对于传统的表单上传,如果我们上传的是一份大文件的话,那你就在那里干等着吧。如此low的用户体验肯定不是我们想要的。那我们该怎么办呢?这时候我们就要用到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];
//主要用的是FormData对象,它能够构建类似表单的键值对。FormData可以凭空创建一个对象,然后往这个对象里面添加数据,然后直接提交
            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
<!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 = $("#img")[0].files[0];
            var form = new FormData();
            form.append("k1", "v1");
            form.append("fff", fileObj);

            $.ajax({
                type:\'POST\',
                url: \'/index\',
                data: form,
                cache: false
                processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success: function(arg){
                    console.log(arg);
                }
            })
        }
 /*cache
    cache设为false可以禁止浏览器对该URL(以及对应的HTTP方法)的缓存。 jQuery通过为URL添加一个冗余参数来实现。
    该方法只对GET和HEAD起作用,然而IE8会缓存之前的GET结果来响应POST请求。 这里设置cache: false是为了兼容IE8。

contentType
    jQuery中content-type默认值为application/x-www-form-urlencoded, 因此传给data参数的对象会默认被转换为query string(见HTTP 表单编码 enctype)。
    我们不需要jQuery做这个转换,否则会破坏掉multipart/form-data的编码格式。 因此设置contentType: false来禁止jQuery的转换操作。

processData
    jQuery会将data对象转换为字符串来发送HTTP请求,默认情况下会用 application/x-www-form-urlencoded编码来进行转换。 我们设置contentType: false后该转换会失败,因此设置processData: false来禁止该转换过程。
    我们给的data就是已经用FormData编码好的数据,不需要jQuery进行字符串转换。
    */
    </script>
</body>
</html>
     
HTML - jQuery

  jQuery文件上传方式依赖于FormData对象, 这是XMLHttpRequest Level 2接口, 需要 IE 10+, Firefox 4.0+, Chrome 7+, Safari 5+, Opera 12+

这意味着对于低版本浏览器只能使用直接提交文件表单的形式, 但提交大文件表单页面会长时间不响应,如果希望在低版本浏览器中解决该问题, 就只能使用别的方式来实现了,比如很多支持多文件和上传进度的Flash插件。

iframe上传

在HTML5没有出现之前,只能用iframe来实现“异步上传”。用户点击submit时,动态插入一个iframe元素

<!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;  //iframe加载完成后立马执行
            document.getElementById(\'my_form\').target = \'my_iframe\'; //,它为表单添加target属性,指向动态插入的iframe窗口,这使得上传结束后,服务器将结果返回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
<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上传示例
$(\'#upload_iframe\').load(function(){
                    var iframeContents = this.contentWindow.document.body.innerText;
                    iframeContents = JSON.parse(iframeContents);
                   
                })
View Code
function bindChangeAvatar1() {
            $(\'#avatarImg\').change(function () {
                var file_obj = $(this)[0].files[0];
                $(\'#prevViewImg\')[0].src = window.URL.createObjectURL(file_obj)
            })
        }

        function bindChangeAvatar2() {
            $(\'#avatarImg\').change(function () {
                var file_obj = $(this)[0].files[0];
                var reader = new FileReader();
                reader.readAsDataURL(file_obj);
                reader.onload = function (e) {
                    $(\'#previewImg\')[0].src = this.result;
                };
            })
        }

        function bindChangeAvatar3() {
            $(\'#avatarImg\').change(function () {
                var file_obj = $(this)[0].files[0];
                var form = new FormData();
                form.add(\'img_upload\', file_obj);

                $.ajax({
                    url: \'\',
                    data: form,
                    processData: false,  // tell jQuery not to process the data
                    contentType: false,  // tell jQuery not to set contentType
                    success: function (arg) {

                    }
                })
            })
        }

        function bindChangeAvatar4() {
            $(\'#avatarImg\').change(function () {
                $(this).parent().submit();

                $(\'#upload_iframe\').load(function () {
                    var iframeContents = this.contentWindow.document.body.innerText;
                    iframeContents = JSON.parse(iframeContents);
                    if (iframeContents.status) {
                        $(\'#previewImg\').attr(\'src\', \'/\' + iframeContents.data);
                    }
                })

            })
        }
其他

3、进度条

XMLHttpRequest第二版还定义了一个progress事件,可以用来制作进度条。

首先,在页面中放置一个HTML元素progress。

 <progress id="uploadprogress" min="0" max="100" value="0">0</progress>

然后,定义progress事件的回调函数。

xhr.upload.onprogress = function (event) {
    if (event.lengthComputable) {
      var complete = (event.loaded / event.total * 100 | 0);
      var progress = document.getElementById(\'uploadprogress\');
      progress.value = progress.innerHTML = complete;
    }
  };
View Code

注意,progress事件不是定义在xhr,而是定义在xhr.upload,因为这里需要区分下载和上传,下载也有一个progress事件。

4、图片预览

如果上传的是图片文件,利用File API,我们可以做一个图片文件的预览。这里主要用到FileReader对象

 // 检查是否支持FileReader对象
  if (typeof FileReader != \'undefined\') {
    var acceptedTypes = {
      \'image/png\': true,
      \'image/jpeg\': true,
      \'image/gif\': true
    };
    if (acceptedTypes[document.getElementById(\'upload\').files[0].type] === true) {
      var reader = new FileReader();
      reader.onload = function (event) {
        var image = new Image();
        image.src = event.target.result;
        image.width = 100;
        document.body.appendChild(image);
      };
    reader.readAsDataURL(document.getElementById(\'upload\').files[0]);
    }
  }
View Code

5、拖放上传

最后,利用HTML5的拖放功能,实现拖放上传。

先在页面中放置一个容器,用来接收拖放的文件。

 <div id="holder"></div>

对它设置样式:

 #holder {
    border: 10px dashed #ccc;
    width: 300px;
    min-height: 300px;
    margin: 20px auto;
  }
  #holder.hover {
    border: 10px dashed #0c0;
  }
View Code

拖放文件的代码,主要是定义dragover、dragend和drop这三个事件。

 // 检查浏览器是否支持拖放上传。
  if(\'draggable\' in document.createElement(\'span\')){
    var holder = document.getElementById(\'holder\');
    holder.ondragover = function () { this.className = \'hover\'; return false; };
    holder.ondragend = function () { this.className = \'\'; return false; };
    holder.ondrop = function (event) {
      event.preventDefault();
      this.className = \'\';
      var files = event.dataTransfer.files;
      // do something with files
    };
  }
View Code

参考:http://www.ruanyifeng.com/blog/2012/08/file_upload.html

参考:http://www.cnblogs.com/wupeiqi/articles/5702910.html

以上是关于上传文件的主要内容,如果未能解决你的问题,请参考以下文章

将存储在内存中的文件上传到s3

JS创建文件并上传服务器

ajaxFileUpload上传带参数文件及JS验证文件大小

android的自带的httpClient 怎么上传文件

大文件上传下载实现思路,分片断点续传代码实现,以及webUpload组件

如何通过 HttpWebRequest 上传文件?