首页 > 开发 > 前端 > 正文

Web文件上传体验与深入

2017-07-12 18:00:33  来源:慕课网

暑假在学校又开始繁忙的开发任务啦。这次遇到了文件上传,研究了一下步骤之后,我收获挺多的,所以就写一篇博客啦!

知识点 form表单 HTTP 中MIME类型 input 的file控件 HTML5 FileApi 使用node.js 接收form data 类型
<!-- more --> form表单

首先仔细探讨一下form表单,现在我们前后端的数据传递基本都是用Ajax而不是直接使用form表单,所以我们对于表单的认识非常的不足。

一个表单里面有form元素,但action为空白的时候,刷新页面,会弹出一个警告框提示你已经填入表单,刷新数据将会丢失。
如果一个表单里面有一个type="submit"的button、或者type="submit"的input,或者type="image"的input,点击则会触发表单提交动作。即使你使用了return false,按钮也还是会提交submit动作。<button></button>的默认属性是submit,所以form内有没有设置type的button,也会触发submit()事件

form表单的属性 选择几个比较重要的属性说明 属性值 说明 accept-charset 服务器能够处理的字符集,多个字符集用空格分割 action 接受请求的URL,该值可以被form元素中的input或button元素的formaction属性覆盖 enctype 在发送到服务器之前应该如何对表单数据进行编码。 method 要发送的HTTP请求类型,通常是“get”或“post”,该值可以被form元素中的input或button元素的formmethod属性覆盖 submit() 提交表单 target 用于发送请求和接收响应的窗口名称,该值可以被form元素中的input或button元素的formtarget属性覆盖 enctype 有三个属性值 application/x-www-form-urlencoded这是默认值,在发送前,所有字符都会进行编码,空格转换为 "+" 加号,特殊符号转换为 ASCII HEX 值 multipart/form-data不对字符编码。
在使用包含文件上传控件的表单时,必须使用该值。 text/plain空格转换为 "+" 加号,但不对特殊字符编码。 target属性规定在何处打开 action URL,有5个属性 _blank在新窗口中打开。 _self默认。在相同的框架中打开。 _parent在父框架集中打开。 _top在整个窗口中打开 framename在指定的框架中打开。

一般设置target为一个隐藏的iframe,达到无刷新上传数据的效果

HTTP协议 MIME类型

MIME类型是一种通知客户端其接收文件的多样性的机制:文件后缀名在网页上并没有明确的意义。因此,使服务器设置正确的传输类型非常重要,所以正确的MIME类型与每个文件一同传输给服务器。在网络资源进行连接时,浏览器经常使用MIME类型来决定执行何种默认行为。

MIME的组成结构非常简单;由类型与子类型两个字符串中间用“/”分隔而组成。并不允许空格存在。type 表示可以被分为复数子类的独立类型。subtype 表示细分后的每个类型。

type/subtype


简而言之就是MIME类型使得HTTP协议能够传输文件,MIME规定了用于表示各种各样的数据类型的符号化方法。 此外,在万维网中使用的HTTP协议中也使用了MIME的框架,标准被扩展为互联网媒体类型。

MIME与multipart/form-data的关系

Multipart types 表示细分领域的文件类型的种类,经常对应不同的 MIME类型。这是 复合文 件的一种表现方式。对于 multipart/form-data 的例外部分,可以使用HTML Forms 和 POST 方法来解决,以及使用状态码206 Partial Content 来发送整个文件的子集,而HTTP不能处理的复合文件使用一个特殊的方式:将信息直接传送给浏览器(这时可能会建立一个“另存为”窗口,但是却不知道如何去显示内联文件。

multipart/form-data 可用于HTML表单从浏览器发送信息给服务器。 作为多部分文档格式,它由边界线(一个由'--'开始的字符串)划分出的不同部分组成。每一部分有自己的实体,以及自己的 HTTP 请求头,Content-Disposition和 Content-Type 用于文件上传领域,最常用的 (Content-Length 因为边界线作为分隔符而被忽略)。

在这我们可以看到HTTP请求头中包含了Content-Type:multipart/form-data,而boundary则表示每个数据之间的分割线,方便服务器分割不同的数据。比如图中的name="file"和name="flag"
一言以蔽之,MIME类型使得HTTP协议能够传输文件,multipart/form-data则使得form表单能够与服务器与客户端之间传递文件

input的file控件

标准写法<input type="file" name="file" multiple accept="image/gif,image/jpeg,image/jpg,image/png">其中multiple表示可选多个值。accept属性则表示能够通过文件上传提交的文件类型出现一次input[type="file"]则创建一个FileUpload对象就会被创建。

该元素的 value 属性保存了用户指定的文件的名称,但是当包含一个 file-upload 元素的表单被提交的时候,浏览器会向服务器发送选中的文件的内容而不仅仅是发送文件名

那如何获得文件的具体信息呢?

文件一开始其实 会被放到本地的一个临时文件夹内,然后我们可以使用一些方法来获得这个文件对象。
File对象的属性

name : 文件名,不包含路径。 type : 文件类型。图片类型的文件都会以 image/ 开头,可以由此来限制只允许上传图片。 size : 文件大小。可以根据文件大小来进行其他操作。 lastModified : 文件最后修改的时间。
<input type="file" id="files" multiple> 
<script> 
var elem = document.getElementById('files'); 
elem.onchange = function (event) { 
var files = event.target.files; 
for (var i = 0; i < files.length; i++) {
     // 文件类型为 image 并且文件大小小于 200kb 
 if(files[i].type.indexOf('image/') !== -1 && files[i].size < 204800){
  console.log(files[i].name); 
  } 
} 
}
</script>
FileReader对象

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
其中File对象可以是来自用户在一个<input>元素上选择文件后返回的FileList对象,也可以来自拖放操作生成的 DataTransfer对象,还可以是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后返回结果.

创建一个FileReader()对象

let reader = new FileReader();
该对象有以下方法:

abort:中断读取操作。

readAsArrayBuffer:读取文件内容到ArrayBuffer对象中。

readAsBinaryString:将文件读取为二进制数据。

readAsDataURL:将文件读取为data: URL格式的字符串。

readAsText:将文件读取为文本。

最常见的应用就是上传图片的预览功能。

< input type="file" id="files" accept="image/jpeg,image/jpg,image/png">
< img src="blank.gif" id="preview">
<scrtpt>
    var elem = document.getElementById('files'),
    img = document.getElementById('preview');
elem.onchange = function () {
    var files = elem.files,
        reader = new FileReader();
    if (files && files[0]) {
        reader.onload = function (ev) {
            img.src = ev.target.result;
        }
        reader.readAsDataURL(files[0]);
    }
}
node.js接收formdata数据

formdata数据和平常的数据并不一样,需要经过特殊的处理才能被后端正常接收。
这里我使用了 multiparty这个包来处理
代码如下

var http = require('http');
var multiparty = require('multiparty');

http.createServer(function (req, res) {
    var form = new multiparty.Form();
    form.parse(req, function (err, fields, files) {
       console.log(fields);
    });
}).listen(3000); 
参考链接 HTTP协议之multipart/form-data请求分析 RFC2616 3.7.2 HTML5 File api fileReader对象 multiparty