上传文件的FormData()和Content-Type探究

1,615 阅读6分钟

问题列表

1:上传文件的FormData

1:Content-Type

一:为什么要常使用new FormData()方式来进行数据的操作上传呢;

先解释一下文件上传这个过程是怎么一回事。因为浏览器本身的限制,浏览器是不能直接操作文件系统的,需要通过浏览器所暴露出来的统一接口,由用户主动授权发起来访问文件动作,然后读取文件内容进指定内存里,最后执行提交请求操作,将内存里的文件内容数据上传到服务端,最后服务端解析前端传来的数据信息后存入文件里。

最简单的form上传是这样

<form method="POST" enctype="multipart/form-data">
  <input type="file" name="file" value="请选择文件"><br />
  <input type="submit">
</form>

再来看看从前端传来的被上传到服务端的文件数据

------WebKitFormBoundary7YGEQ1Wf4VuKd0cE
Content-Disposition: form-data; name="file"; filename="index.html"
Content-Type: text/html

<html>
  <head>
    <title>上传文件</title>
  </head>
  <body>
    <form method="POST" enctype="multipart/form-data">
      <input type="file" name="file" value="请选择文件"><br />
      <input type="submit">
    </form>
  </body>
</html>
------WebKitFormBoundary7YGEQ1Wf4VuKd0cE--

从上面被上传到服务端的数据可以看出相比于客户端本地的文件中多了几行内容,先是第一行和最后一行的WebKitFormBoundary 码,第二行的ContentDisposition,该行包含一些文件基本信息,还有第三行文件内容类型,所以后端如果要获取到正确的文件内容则需要自己去除由浏览器在上传时所添加的进来的几行内容,而保留有效文件内容后进行写文件操作,完成上传目的。

从上面的最简单的实现中可以看出以下几个点 。

  1. 前端文件上传实际是文件内容的传递,是数据的传递,并非我们最常使用的文件拷贝与复制操作。
  2. 传递过程中要进行编码来制定发送的文件数据规则,以便于后端能够实现一套对应的解析规则。
  3. 传递的数据规则里包含所传递文件的基本信息 ,如文件名与文件类型,以便后端写出正确格式的文件。

上面的相比于Form表单的提交,使用了浏览器的XMLHttpRequest自定义的提交方式,也就是俗称的AJAX技术。但是使用这种提交方式没有设置编码 enctype="multipart/form-data" 类型,如果直接将文件内容上传,会导致后端在解析Form表单上传的文件时与Ajax上传的不一致,所以为了后端能够使用相同的代码就能解析前端这两种提交方式,所以前端需要自行格式化文件内容。在格式化的过程中,则需要通过浏览器自身提供的FormData构造函数来实例化的一个文件fd,然后使用实例的append方法将文件内容插入进去,最后利用XMLHttpRequest的实例做出发送动作。

Form表单的意义

首先来想一想,Form表单对文件上传的文件内容做了什么,它格式化了文件内容,在请求时以特定的格式发送了数据至服务器,

再思考一下,这样的格式化的目的又是什么?先看看格式化后的内容,它包含了一个文件的全部信息,如格式,文件名,文件内容均已特定的字段或者位置出现,所以格式化的目的就是在制定一种规范,一种约定俗成的规范,无论哪一个项目或是那一个网站它的文件上传如果选择Form表单编码类型,它均是一种输出。

二:Content-Type

上传文件是一个前端常见的需求,但是为什么上传文件必须使用content-type:multipart/form-data作为请求头?

先看MDN是怎么解释的Web/HTTP/Headers/Content-Type

In a POST request, resulting from an HTML form submission, the Content-Type of the request is specified by the enctype attribute on the form element
<form action="/" method="post" enctype="multipart/form-data">
  <input type="text" name="description" value="some text">
  <input type="file" name="myFile">
  <button type="submit">Submit</button>
</form>

//The request looks something like this (less interesting headers are omitted here):

POST /foo HTTP/1.1
Content-Length: 68137
Content-Type: multipart/form-data; boundary=---------------------------974767299852498929531610575

-----------------------------974767299852498929531610575
Content-Disposition: form-data; name="description"

some text
-----------------------------974767299852498929531610575
Content-Disposition: form-data; name="myFile"; filename="foo.txt"
Content-Type: text/plain

(content of the uploaded file foo.txt)
-----------------------------974767299852498929531610575--

请求头的多种类型详情请看维基百科

multipart/form-data最初由rfc1867 Form-based File Upload in HTML文档提出。

Since file-upload is a feature that will benefit many applications, this proposes an
extension to HTML to allow information providers to express file upload requests uniformly, and a MIME compatible representation for file upload responses.

1867文档简介中说明文件上传作为一种常见的需求,在目前(1995年)的html中的form表单格式中还不支持,因此提出了一种兼容此需求的mime type

The encoding type application/x-www-form-urlencoded is inefficient for sending large quantities of binary data or text containing non-ASCII characters.  Thus, a new media type,multipart/form-data, is proposed as a way of efficiently sending the
values associated with a filled-out form from client to server.

1867文档中也写了为什么要新增一个类型,而不使用旧有的application/x-www-form-urlencoded:因为此类型不适合用于传输大型二进制数据或者包含非ASCII字符的数据。平常我们使用这个类型都是把表单数据使用url编码后传送给后端,二进制文件当然没办法一起编码进去了。所以multipart/form-data就诞生了,专门用于有效的传输文件。

On the other hand, the 'multipart' mechanisms are well established, simple to implement on
both the sending client and receiving server, and as efficient as
other methods of dealing with multiple combinations of binary data.

文档中还解释了为什么要沿用multipart这个机制。multipart机制的定义在 rfc1341 7.2

但这个1867文档中对 multipart/form-data 的具体格式并没有写的非常详细,只在第六部分的【Examples】当中给了一个很基本的范例,所以 1998 年又有了一份新的 rfc2388 Returning Values from Forms: multipart/form-data来阐明不仅在HTTP协议下传输文件、而且使用邮件传输文件时 multipart/form-data 中的各个部分的具体格式[1]。分别对3.Definition of multipart/form-data multipart/form-data的定义;4.Use of multipart/form-data具体使用格式和方法做了更详细的阐述,其中第四章中包括

参考

RFC 1867: Form-based File Upload in HTML

表单提交中的 x-www-form-urlencoded 和 multipart/form-data

上传文件multipart/form-data深入解析

链接:

若想查看koa中转文件上传至Java可点击我的另一偏文章上传文件 koa 中间件转发文件至Java