We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
最近项目有用到文件上传,发现对这一块内容不是很了解,所以花时间整理一份这方面的知识体系
常见的HTTP请求报文头属性
Accept
请求报文可通过一个“Accept”报文头属性告诉服务端客户端接受什么类型的响应。Accept属性的值可以为一个或多个MIME类型的值,关于MIME类型,大家请参考:http://en.wikipedia.org/wiki/MIME_type
Accept: application/javascript Accept: application/json Accept: application/x-www-form-urlencodedtext/css Accept: text/htm Accept: image/pn Accept: multipart/form-data // ...
Cookie
客户端的Cookie就是通过这个报文头属性传给服务端的哦!如下所示:
Cookie: $Version=1; Skin=new;jsessionid=5F4771183629C9834F8382E23BE13C4C
Cache-Control
对缓存进行控制,如一个请求希望响应返回的内容在客户端要被缓存一年,或不希望被缓存就可以通过这个报文头达到目的。
Cache-Control: no-cache // 不缓存 Cache-Control: max-age=600 // 缓存内容将在xxx秒后失效
Content-Type
Content-Type用于指定内容类型,一般是指网页中存在的Content-Type,Content-Type属性指定请求和响应的HTTP内容类型。如果未指定 ContentType,默认为text/html。 常见的 Content-Type 如下:
Content-Type: text/html Content-Type: text/plain Content-Type: text/css Content-Type: text/javascript Content-Type: application/x-www-form-urlencoded Content-Type: multipart/form-data Content-Type: application/json Content-Type: application/xml
Content-Type 是重点,对我们理解数据上传,或文件上传有帮助,下面重点讲一下 Content-Type
application/x-www-form-urlencoded
application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式 比如一个简单的表单提交
<form enctype="application/x-www-form-urlencoded" action="http://homeway.me/post.php" method="POST"> <input type="text" name="name" value="homeway"> <input type="text" name="key" value="nokey"> <input type="submit" value="submit"> </form>
请求主体如下:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding:gzip, deflate Accept-Language:zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,gl;q=0.2,de;q=0.2 Cache-Control:no-cache Connection:keep-alive Content-Length:17 Content-Type:application/x-www-form-urlencoded
那么服务器收到的raw body会是,name=homeway&key=nokey,在php中,通过$_POST就可以获得数组形式的数据。
text/xml 微信用的是这种数据格式发送请求的。
POST http://www.homeway.me HTTP/1.1 Content-Type: text/xml <?xml version="1.0"?> <resource> <id>123</id> <params> <name> <value>homeway</value> </name> <age> <value>22</value> </age> </params> </resource>
multipart/form-data
multipart/form-data用在发送文件的POST包。 通过控制台,可以看到发送一个文件的数据内容如下:
POST http://www.homeway.me HTTP/1.1 Content-Type:multipart/form-data; boundary=------WebKitFormBoundaryOGkWPJsSaJCPWjZP ------WebKitFormBoundaryOGkWPJsSaJCPWjZP Content-Disposition: form-data; name="key2" 456 ------WebKitFormBoundaryOGkWPJsSaJCPWjZP Content-Disposition: form-data; name="key1" 123 ------WebKitFormBoundaryOGkWPJsSaJCPWjZP Content-Disposition: form-data; name="file"; filename="index.png"
这里Content-Type告诉我们,发包是以multipart/form-data格式来传输,另外,还有boundary用于分割数据。 当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据,分割成一个一个chunk发送给服务端, 那么--用于区分数据快,而后面的数据 WebKitFormBoundaryOGkWPJsSaJCPWjZP 就是标示区分包作用。
当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据,分割成一个一个chunk发送给服务端
更多请求报文属性请参考 http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
常见的HTTP响应报文头属性
请求报文头也有个 Cache-Control ,请求的 Cache-Control 用于告诉服务器我需要缓存这个请求资源,服务端接受到这个属性后也给客服端响应一个 Cache-Control 属性,通过该报文头属告诉客户端如何控制响应内容的缓存。 比如设置了 Cache-Control: max-age=3600 让客户端对响应内容缓存3600秒,也即在3600秒内,如果客户再次访问该资源,直接从客户端的缓存中返回内容给客户,不要再从服务端获取(当然,这个功能是靠客户端实现的,服务端只是通过这个属性提示客户端“应该这么做”,做不做,还是决定于客户端,如果是自己宣称支持HTTP的客户端,则就应该这样实现)。
Cache-Control: max-age=3600
ETag
一个代表响应服务端资源(如页面)版本的报文头属性,如果某个服务端资源发生变化了,这个ETag就会相应发生变化。它是Cache-Control的有益补充,可以让客户端“更智能”地处理什么时候要从服务端取资源,什么时候可以直接从缓存中返回响应。
ETag: "737060cd8c284d8af7ad3082f209582d"
Set-Cookie
服务端可以设置客户端的Cookie,其原理就是通过这个响应报文头属性实现的:
Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
HTML5 添加了一些强大的 File API
FileList 对象针对表单的 file 控件。当用户通过 file 控件选取文件后,这个控件的 files 属性值就是 FileList 对象。它在结构上类似于数组,包含用户选取的多个文件。如果 file 控件没有设置 multiple 属性,那么用户只能选择一个文件,FileList 对象也就只有一个元素了。
<input type='file' /> <script> document.querySelector('input').onchange = function() { console.log(this.files); }; </script>
由控制台可以看到 FileList 是一个数组,数组包含文件的一些信息
我们看到一个 FileList 对象包含了我们选中的 File 对象,那么一个 File 又有哪些属性呢?我们可以打印出来看看。
name:文件名,该属性只读。
size:文件大小,单位为字节,该属性只读。
type:文件的 MIME 类型,如果分辨不出类型,则为空字符串,该属性只读。
lastModified:文件的上次修改时间,格式为时间戳。
lastModifiedDate:文件的上次修改时间,格式为 Date 对象实例。
上图中我们看到,File 对象是继承自 Blob 对象的,Blob 又是什么鬼? Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。 生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。
var a = ["hello", "world"]; var myBlob = new Blob(a, { "type" : "text/xml" }); console.log(myBlob);
Blob 对象有两个只读属性:
size:二进制数据的大小,单位为字节。(文件上传时可以在前端判断文件大小是否合适)
type:二进制数据的 MIME 类型,全部为小写,如果类型未知,则该值为空字符串。(文件上传时可以在前端判断文件类型是否合适)
FileReader API 才是我们接下去完成一些任务的关键。FileReader API 用于读取文件,即把文件内容读入内存。它的参数是 File 对象或 Blob 对象。
var reader = new FileReader(); reader.abort();
URL 对象居然也属于File API ,我也很吃惊,不过下面的API估计我们或多或少有用过
var objecturl = window.URL.createObjectURL(blob);
上面的代码会对二进制数据生成一个 URL,这个 URL 可以放置于任何通常可以放置 URL 的地方,比如 img 标签的 src 属性。需要注意的是,即使是同样的二进制数据,每调用一次 URL.createObjectURL 方法,就会得到一个不一样的 URL。 这个 URL 的存在时间,等同于网页的存在时间,一旦网页刷新或卸载,这个 URL 就失效。(File 和 Blob 又何尝不是这样呢)除此之外,也可以手动调用 URL.revokeObjectURL 方法,使 URL 失效。
介绍了那么多,实际用到的知识很少,但有个大概的内容体系才不多对自己写的代码一知半解不是吗。下面我们用个文件上传的demo实际操作一下:
我们用 form 表单和 ajax 方式来分别实现文件上传
<section> <h1>form 表单方式</h1> <form method="POST" action="/api/uploadFile" enctype="multipart/form-data"> <p>file upload</p> <span>picName:</span><input name="picName" type="text" /><br/> <input name="file" type="file" /><br/><br/> <button type="submit">submit</button> </form> </section> <section> <h1>formData 方式</h1> <input id="J_file_type1" name="file" type="file" /><br/><br/> <button id="J_btn_upload_type1">上传</button> </section>
点击页面的 <button type="submit">submit</button> 就实现了文件上传。 form 表单设置了 action 上传路径 enctype 上传类型(表现在请求头中)这个在文章的最开始部分咱们也就介绍了就不多说了。 form 表单上传文件有个不好的地方是form 表单提交会刷新页面,也就对用户很不友好了,下面咱们再用 formData 方式 来实现文件上传
<button type="submit">submit</button>
const file = document.querySelector('#J_file_type1').files[0] const formData = new FormData() // 建立一个upload表单项,值为上传的文件 formData.append('file', file) formData.append('name', file.name) const xhr = new XMLHttpRequest() xhr.open('POST', '/api/uploadFile') // 定义上传完成后的回调函数 xhr.onload = function () { if (xhr.status === 200) { alert('上传成功') } else { alert('出错了') } } xhr.send(formData);
这里我们用到了 FormData 来实现文件上传。 FormData对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。其主要用于发送表单数据,但亦可用于发送带键数据(keyed data),而独立于表单使用。如果表单enctype属性设为multipart/form-data ,则会使用表单的submit()方法来发送数据,从而,发送数据具有同样形式。
如果不使用FormData对象的情况下,通过AJAX序列化和提交表单也是可以实现表单上传,不过这也太变态了,因为要自己序列化上面提到的文件上传的请求主体
感兴趣的可以点开链接查看 点开链接查看
1、获取项目分支
git clone https://github.com/ZengTianShengZ/My-Blog.git git checkout -b demo-file-upload origin/demo-file-upload
2、项目构建
cd demo // 切换至 demo 目录 npm install node app.js
The text was updated successfully, but these errors were encountered:
No branches or pull requests
浏览器文件上传
最近项目有用到文件上传,发现对这一块内容不是很了解,所以花时间整理一份这方面的知识体系
一、预备知识
1、HTTP 请求和响应
请求
常见的HTTP请求报文头属性
请求报文可通过一个“Accept”报文头属性告诉服务端客户端接受什么类型的响应。Accept属性的值可以为一个或多个MIME类型的值,关于MIME类型,大家请参考:http://en.wikipedia.org/wiki/MIME_type
客户端的Cookie就是通过这个报文头属性传给服务端的哦!如下所示:
对缓存进行控制,如一个请求希望响应返回的内容在客户端要被缓存一年,或不希望被缓存就可以通过这个报文头达到目的。
Content-Type用于指定内容类型,一般是指网页中存在的Content-Type,Content-Type属性指定请求和响应的HTTP内容类型。如果未指定 ContentType,默认为text/html。
常见的 Content-Type 如下:
Content-Type 是重点,对我们理解数据上传,或文件上传有帮助,下面重点讲一下 Content-Type
application/x-www-form-urlencoded
application/x-www-form-urlencoded是常用的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式
比如一个简单的表单提交
请求主体如下:
那么服务器收到的raw body会是,name=homeway&key=nokey,在php中,通过$_POST就可以获得数组形式的数据。
text/xml
微信用的是这种数据格式发送请求的。
multipart/form-data
multipart/form-data用在发送文件的POST包。
通过控制台,可以看到发送一个文件的数据内容如下:
这里Content-Type告诉我们,发包是以multipart/form-data格式来传输,另外,还有boundary用于分割数据。
当文件太长,HTTP无法在一个包之内发送完毕,就需要分割数据,分割成一个一个chunk发送给服务端
,那么--用于区分数据快,而后面的数据 WebKitFormBoundaryOGkWPJsSaJCPWjZP 就是标示区分包作用。
更多请求报文属性请参考 http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
响应
常见的HTTP响应报文头属性
请求报文头也有个 Cache-Control ,请求的 Cache-Control 用于告诉服务器我需要缓存这个请求资源,服务端接受到这个属性后也给客服端响应一个 Cache-Control 属性,通过该报文头属告诉客户端如何控制响应内容的缓存。
比如设置了
Cache-Control: max-age=3600
让客户端对响应内容缓存3600秒,也即在3600秒内,如果客户再次访问该资源,直接从客户端的缓存中返回内容给客户,不要再从服务端获取(当然,这个功能是靠客户端实现的,服务端只是通过这个属性提示客户端“应该这么做”,做不做,还是决定于客户端,如果是自己宣称支持HTTP的客户端,则就应该这样实现)。一个代表响应服务端资源(如页面)版本的报文头属性,如果某个服务端资源发生变化了,这个ETag就会相应发生变化。它是Cache-Control的有益补充,可以让客户端“更智能”地处理什么时候要从服务端取资源,什么时候可以直接从缓存中返回响应。
服务端可以设置客户端的Cookie,其原理就是通过这个响应报文头属性实现的:
2、input file 知识
HTML5 添加了一些强大的 File API
FileList
FileList 对象针对表单的 file 控件。当用户通过 file 控件选取文件后,这个控件的 files 属性值就是 FileList 对象。它在结构上类似于数组,包含用户选取的多个文件。如果 file 控件没有设置 multiple 属性,那么用户只能选择一个文件,FileList 对象也就只有一个元素了。
由控制台可以看到 FileList 是一个数组,数组包含文件的一些信息
File
我们看到一个 FileList 对象包含了我们选中的 File 对象,那么一个 File 又有哪些属性呢?我们可以打印出来看看。
name:文件名,该属性只读。
size:文件大小,单位为字节,该属性只读。
type:文件的 MIME 类型,如果分辨不出类型,则为空字符串,该属性只读。
lastModified:文件的上次修改时间,格式为时间戳。
lastModifiedDate:文件的上次修改时间,格式为 Date 对象实例。
Blob
上图中我们看到,File 对象是继承自 Blob 对象的,Blob 又是什么鬼?
Blob(Binary Large Object)对象代表了一段二进制数据,提供了一系列操作接口。其他操作二进制数据的 API(比如 File 对象),都是建立在 Blob 对象基础上的,继承了它的属性和方法。
生成 Blob 对象有两种方法:一种是使用 Blob 构造函数,另一种是对现有的 Blob 对象使用 slice 方法切出一部分。
Blob 对象有两个只读属性:
size:二进制数据的大小,单位为字节。(文件上传时可以在前端判断文件大小是否合适)
type:二进制数据的 MIME 类型,全部为小写,如果类型未知,则该值为空字符串。(文件上传时可以在前端判断文件类型是否合适)
FileReader
FileReader API 才是我们接下去完成一些任务的关键。FileReader API 用于读取文件,即把文件内容读入内存。它的参数是 File 对象或 Blob 对象。
URL
URL 对象居然也属于File API ,我也很吃惊,不过下面的API估计我们或多或少有用过
上面的代码会对二进制数据生成一个 URL,这个 URL 可以放置于任何通常可以放置 URL 的地方,比如 img 标签的 src 属性。需要注意的是,即使是同样的二进制数据,每调用一次 URL.createObjectURL 方法,就会得到一个不一样的 URL。
这个 URL 的存在时间,等同于网页的存在时间,一旦网页刷新或卸载,这个 URL 就失效。(File 和 Blob 又何尝不是这样呢)除此之外,也可以手动调用 URL.revokeObjectURL 方法,使 URL 失效。
二、文件上传
介绍了那么多,实际用到的知识很少,但有个大概的内容体系才不多对自己写的代码一知半解不是吗。下面我们用个文件上传的demo实际操作一下:
我们用 form 表单和 ajax 方式来分别实现文件上传
1、form 表单方式
点击页面的
<button type="submit">submit</button>
就实现了文件上传。form 表单设置了 action 上传路径 enctype 上传类型(表现在请求头中)这个在文章的最开始部分咱们也就介绍了就不多说了。
form 表单上传文件有个不好的地方是form 表单提交会刷新页面,也就对用户很不友好了,下面咱们再用 formData 方式 来实现文件上传
2、formData 方式
这里我们用到了 FormData 来实现文件上传。
FormData对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。其主要用于发送表单数据,但亦可用于发送带键数据(keyed data),而独立于表单使用。如果表单enctype属性设为multipart/form-data ,则会使用表单的submit()方法来发送数据,从而,发送数据具有同样形式。
3、如果不使用 formData 方式呢
如果不使用FormData对象的情况下,通过AJAX序列化和提交表单也是可以实现表单上传,不过这也太变态了,因为要自己序列化上面提到的文件上传的请求主体
感兴趣的可以点开链接查看 点开链接查看
运行项目
1、获取项目分支
2、项目构建
The text was updated successfully, but these errors were encountered: