在项目中有个需求是需要上传文件,项目中使用的是antd-vue UI库,用法就仅仅是将后端给的文件上传接口放到antd-vue的a-upload组件的action属性上面。然后将后端返回的文件网络资源再次提交到后端。这样功能是实现了,但是具体发生了什么确实没有什么认知。在这里把我认为的需要知道的文件上传的前端知识以及我用这些知识来解决了哪些问题写一篇随笔加以记录。
1.<input type="file">
文件上传是基于这个标签实现的,通过约定这个标签的属性用来控制文件上传的,比如控制文件的类型、change事件等,具体可查看MDN文档。
在这个标签的change事件中,可以查看到已经选择的文件的信息:
<body> 上传文件:<input type="file" id="file" multiple> <script> let file = document.getElementById("file") file.onchange = function (e) { console.log("当前元素", e); console.log("文件列表", e.target.files); } </script> </body>
通过控制台可以查看到当前node节点的信息以及这个file类型的input标签中的文件信息:
node节点信息这里不谈,这里主要了解文件信息的这几个属性(这些属性都属于JavaScript API中的File API):
- name:本地系统中的文件名
- size:以字节记的文件大小——这个属性可以用来前端控制文件上传的大小
- type:包含文件MIME类型的字符串
- lastModifiedDate:表示文件最后修改事件的字符串。这个属性只有Chrome实现了
2.FileReader类型
FileReader类型属于File API,作用是从文件中实际读取数据。这个功能主要用于需要前端读取上传文件内容的需求上。
在项目中我遇到过读取csv文件中的内容并将其读取出来填充到表格中,这个功能就用到了Fi了Reader类型。
(1)读取文件的方法
FileReader类型表示一种异步文件读取机制。可以把FileReader想象成XMLHttpRequest,只不过是用于从文件系统读取文件,而不是从服务器中。FileReader类型提供了几个读取文件数据的方法:
-
- (1)readAsText(file,encoding):从文件中读取纯文本内容并保存在result属性中。
- ① file是需要读取的文件;
- ② encoding表示编码(如果读取的文件可能有中文乱码的情况,可以填"GB2312")
- ③ 使用场景:在某些需求下可能需要前端获取到文件上传的文本中的内容用来操作(当然这也可以把文件读取放在后端操作,然后通过后端接口返回读取到的数据)。上面说的读取内容并填充就用到了这个功能,这里用原生的<input type = "file">实现一遍:
-
1 <body> 2 上传文件:<input type="file" id="file"> 3 </body> 4 5 <script> 6 let file = document.getElementById("file") 7 file.onchange = function (e) { 8 let reader = new FileReader(); // 实例化FileReader对象 9 reader.readAsText(e.target.files[0], 'GB2312') // 这里第二个参数是可选的,且编码类型需要根据不同的文件类型抉择,下面的CSV文件与TXT文件就分别用了GB2312(CSV)和没有传入第二个参数 10 reader.onload = (res) => { // onload表示文件读取成功时的回调 12 console.log("文件读取内容", res); 13 console.log(res.target.result.split(/[( ) ]+/)); // 这里读取的CSV文件每一行都会带一个换行符,这里使用字符串拆解成根据行数分开的数组 14 } 15 } 16 </script>
- 文件内容及读取结果:
- 文件内容读取结果:
- 文件内容读取结果:
-
- (1)readAsText(file,encoding):从文件中读取纯文本内容并保存在result属性中。
-
-
-
- 说明:
- readAsText第一个参数传入的就是上面说的<input type = "file">标签的文件上传的文件列表中的某个文件(就是包含size、name、type、lastModifiedDate属性的文件)。对应了第九行的代码
- 这里读取CSV的结果以及txt类型的文件中,换行符将会包含在其中,我这里使用正则表达式按行分割了读取到的内容。因此这里需要根据实际情况自行处理
- 在antd-vue的upload组件的change属性,实现同理,只不过change属性将包含size、name、type、lastModifiedDate属性的文件这个属性放到了change回调函数中默认传入的{file,files}属性中了,如果使用的是这个组件的话,在change回调中打印一下两个属性找到即可
- 说明:
- (2)ReadAsDataURL(file):读取文件并将内容的数据URL保存在result中。
- 读取到的结果可以直接用于img标签的src属性:
-
1 <body> 2 上传文件:<input type="file" id="file"> 3 <!-- 用于预览的img标签 --> 4 <img id="show-img" src="" alt=""> 5 </body> 6 7 <script> 8 let file = document.getElementById("file") 9 file.onchange = function (e) { 10 let reader = new FileReader(); // 11 reader.readAsDataURL(e.target.files[0]) 12 13 reader.onload = (res) => { 14 console.log("文件读取内容", res); 15 let img = document.getElementById("show-img") 16 img.src = res.target.result 17 } 18 } 19 </script>
- 图片读取的结果:
-
读取的结果是base64格式的图片数据
-
- 且可以直接用于图片展示:
-
- 读取到的结果可以直接用于img标签的src属性:
- (3)readAsBinaryString(file): 读取文件并将每个字符的二进制数据保存在result属性中
- 这个读取为二进制暂时不知如何使用,待补充
- (4)readAsArrayBuffer(file):读取文件并将文件内容以 ArrayBuffer 形式保存在 result 属性。
- 暂时也没有用过,待补充
-
-
(2)事件
由于这些读取方法是异步的,所以每个FileReader会发布几个事件,最常用的三个是:
- process:(reader.onprocess)每50ms就会触发一次,其与XHR的process事件具有相同的信息:lengthComputable、loaded、total。此外,在在process事件中可以读取FileReader的reasult属性,及时其中尚未包含全部数据
- error:(reader.onerror)由于某种原因无法读取文件时触发。包含了错误信息。只包含一个属性:code。可能的值有:
- 1:未找到文件
- 2:安全错误
- 3:读取被终端
- 4:文件不可读
- 5:编码错误
- load:(reader.onload)在文件成功加载后触发。如果error被触发,则不会触发load事件
3.总结
- 如果只是简单的文件上传,则可以直接使用原生<input type = "file">以及各UI库的upload组件配合后端给的文件上传接口处理即可
- 如果有前端限制上传文件大小的需求,则可以根据change回调中的size属性试情况处理
- FileReader类则常用于读取本地文件内容,如读取txt或者CSV等格式文件的内容,不过需要注意编码以及字符串切割
如果还有再补充