关于一把头发弄明白文件传输这件事

402 阅读6分钟

各个语言都有实现了自己关于文件处理的模块和API,但是自己又好像都从来没重视这块儿,一直都是用多少看多少,能跑就行🌚,直到最近又碰到文件上传下载的功能,决心把JS的文件处理好好给他理明白🚩。

准备知识

Web文件API

由于JS是跑在浏览器中的,所以不能直接访问文件系统,通常需要借助Web API,由用户主动发起授权访问文件,然后将文件内容读入指定内存,至此便可以对文件进行操作

🔗FileBlob

File对象是继承自Blob对象的,拥有Blob对象的属性,除此之外还有

  • name属性---文件名
  • lastModified属性---文件最后一次更改的时间戳 在使用<input type="file">标签时获取到的input.files里保存的就是File对象

Blob(binary large object),就是一个可以保存二进制文件的对象,它可以在浏览器中保存一份临时文件,可以用于生成URI等后续使用。 web API中还提供了一个FileReader对象,它的作用就像它的名字一样,唯一指定用途就是可以从Blob对象中对文件进行读取(直接打印Blob对象是看不到文件内容的),提供了系列方法对Blob对象中的数据进行读取。 这部分在实际操作过程中,还经常会遇到诸如DataUrlbase64BlobUrl,以及ArrayBufferformData等神奇的小东西,它们之间的关系跟转换也是一件很有趣的时间,后面也会去整理一下这部分(flag🚩+1)


请求头ContentType

接着说说请求头中的ContentType字段,这部分属于是HTTP的范畴了,但是作为前端肯定都要知道,只有这样才能在比如联调的时候做到在后端面前理不直气也壮🙊。 每一个HTTP的Headers都可以分为这四部分通用头、请求头、响应头、实体头,注意这跟Chrome中调试工具NetWork中的四栏可不是一一对应的东西,这也是当初困扰过我一段时间的,

IMG_20220225-232323653.png

调试工具里的General跟通用头是两码事儿,这里的只是集中显示了请求地址、请求方法、响应状态等相关基本信息,下面的分别是请求的Headers跟响应的Headers。

随便打开一条就可以看到本次请求/响应的Headers信息,

IMG_20220225-233033430.png

可以看到请求头Accept和实体头ContentType,他们分别表示客户端期望接受的MIME类型以及客户端发动的实体数据的MIME类型,所以它的类型就决定了本次请求携带的请求数据的类型,具体文件类型及对应值可以从这里了解,我们重点关注里面的application/x-www-form-urlencodedmultipart/form-data这两个。

application/x-www-form-urlencoded

当使用HTML中的表单进行提交未指定enctype属性的值时,就会发现默认使用的就是这种方式,将表单值以key=value的形式编码传输,只是简单的表单几个值的提交还行,但是对于文件这种数据较大的类型,如果还使用这种方式显然不太合适,而且对于二进制文件,也不能直接进行URL编码携带,然后就有了multipart/form-data,它就可以有效的进行文件传输

multipart/form-data

截了MDN里的一个请求实例来看,

IMG_20220226-001644574.png 首先就是类型,然后是生成一个以“--”开头的叫boundary的东西,它作为边界线将不同的部分分隔开,每一部分有各自的实体请求头和文件名等信息。


文件传输

终于说回了正题,有了上面这些打底,就可以着手进行文件上传/下载了,其实只要搞清楚这个过程中怎么让文件它传过来/过去,拿到/发送的文件是什么样子存在就可以随便进行读取操作了。

上传

就像上面说的,首先就是要先获取文件,可以借助

<input type="file" value="请选择文件">

input.files就可以获取到File对象保存的文件,这一步我们拿到了文件并且知道了它是啥样儿的,然后就是怎么把它传过去。 很幸运我们有HTTP提供的专车multipart/form-data可以把它送过去,但是这又是表单提交时的一种类型,要上车就得符合人家的规定,所以我们是需要把要上车的文件转换成符合规定的形式,但是不是所有情况下都需要表单,这时候发现还可以借助Web API提供的new formData(),它表示一个表单数据对象,我们可以借用它来将文件格式化,这是所有的条件就都符合,我们就可以顺利发送文件,接受的一方只需要按照ContentType类型说明书就可以取到文件。 上述借助表单上传文件的过程,其实就干了两件事,一件是文件类型的转换,可以借助现成的API游刃有余的进行转换;另一件就是声明这个类型,告诉接收方收到之后如何正确进行解析。 而且不难发现,传输的方式可以远不此一种,只需要保证这一过程中涉及到的发送方跟接收方协商好发送的数据的类型,接收方可以成功对其解析即可达到这一目的。

下载

掌握了上传,下载类似一个道理,在下载时可以通过ResponseType来指定接口返回数据类型,从而按照自己的预期对数据进行处理。 有一个需要注意的是如果指定了类型,有可能自动将非文件数据的报错信息等按照指定类型返回,需要额外处理异常情况。


🖋写在最后

做需求的时候想着回头做完一定要去扒一扒这块儿到底是咋回事儿的,还有常用约定俗成的实现,不想以后继续抓瞎,然后遇到又是一顿Ctrl CV,再加上好久也没写过类似总结,顺带记录一份岂不美妙,没想到这个顺带是拖了最久花了最多时间的,生怕哪里写错,即使,一定还有不对或者表述不对的地方🤭,算了遇到发现了再改,爬去睡觉了。