文章目录
- 前言
- 前后端传输的文件格式主要有哪些
- base64
- formData
- 前端上传方案
- input标签获取文件
- HTML5的API
- 切片上传大文件
- blob数据转成base64
- 未来不间断补充
前言
本文章总结了本人在网上和实际公司项目中遇到的有关前端文件上传功能的知识点,如有更好的方案或者发现错误,欢迎评论区中指出。
文章部分知识来源网上博客、gpt问答,以及b站up主【三十的前端课】的视频
前后端传输的文件格式主要有哪些
base64
base64编码只包含64个字符,它们由大小写字母、数字以及"+", "/"等特殊字符组成,但可以利用这64种字符来实现表示所有的字符。
为什么我们有时候在传输文本或者图片信息给后端时需要转成base64呢?
第一个原因
很多情况下前端的数据编码形式和后端的不一致,数据从一个编码转到另一个编码可能会出问题。例如前端传输的数据采用了UTF-8的编码方式,而后端使用的是GBK的编码方式,在传输的过程中就有可能导致数据传输错误或乱码,导致数据无法正常显示和处理。
当我们转成base64后,编码形式统一了。
那为啥偏偏是base64呢?不能是其他编码形式吗?因为base64很简单,就哪些常规的字符、字母,所以编码和解码的形式很简单,效率高。
第二个原因
如果需要对数据进行加密,加密后的数据也要面临不同编码形式的转换,也容易出现编码问题。当对base64的数据进行加密的时候,加密后的数据不会包含特殊字符和二进制码,能较少的出现编码问题。
缺点
base64也不是无敌的,他有以下缺点:
- 转换后的数据明显多了
- 后端拿到base64要有个解码的过程,对后端来说有点性能上的考虑
formData
这是一个可存有二进制blob的对象,当后端需要key-value的形式传递文件的时候,就可以采用这种方式。
使用方法:
let formDataObj = new FormData() // 实例化
formDataObj.append('fileKey', '123') // api的参数为key,value
formDataObj.append('file', file) // file为file对象的文件,下面会讲
console.log('FormData', formDataObj);
axiosApi(formDataObj) // 发送异步请求
当我们打印formData对象的时候,是看不到内容的
但是我们发起axios的post请求就可以看到
可以在请求头看到请求格式:
后面的表示每个key-value用什么字段区分开,可以在抓包工具中看完整的信息
前端上传方案
input标签获取文件
利用原生input标签就提供了文件上传的能力
<template>
<div class="">
<input type="file" @change="fileChange">
</div>
</template>
<script setup>
const fileChange = (e) => {
let file = e.target.files[0] // 单个文件,所以要取下标
console.log('file', file);
}
</script>
把获取到的上传文件打印出来,可以看到是一个File类型的对象,这个对象中有这个文件的很多信息,例如大小,文件类型。里面的文件本体的存储应该是二进制,但打印是看不到的。
这个File对象是Blob对象的一个子类。都是二进制的存储形式,他们之间可以相互转换。
let blobFile = new Blob([file])
console.log('blobFile', blobFile);
let file = new File([blobFile], 'wenjianming.word')
HTML5的API
利用showDirectoryPicker()
这个api可调出文件上传弹窗
const pickDirectory = async () => {
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
if (entry.kind === "file") {
const file = await entry.getFile();
const text = await file.text();
console.log(text);
}
if (entry.kind === "directory") {
/* for file in this directory do something */
}
}
}
目前发现两个问题:
- 不知道为啥只能选整个文件夹,无法看到某个具体文件
- 必须手动触发函数(例如按钮触发),如果是在例如生命周期这样的钩子中被动触发,会报错。
切片上传大文件
一个大文件上传给后端,过程可能会十分漫长,我们可以采用切分的方式,分步骤上传。
能被切片的对象有FIle与Blob
let sliceFile = file.slice(0, 3000) // file对象可以进行切割
let sliceBlobFile = blobFile.slice(0, 3000) // blob对象可以进行切割
例子:
const postApi = (file) => {
let size = 2 * 1024 * 1024 // 每次切片的大小
let fileSize = file.size // 文件总大小
let current = 0 // 当前已上传大小
while(current < fileSize) {
let formData = new FormData()
formData.append('fileName', file.name)
formData.append('filekey', file.slice(current, current + size)) // key值给后端进行拼接的时候能够找对文件
await axios.post('xxx/xx', formData)
percent.value = Math.min((current / fileSize) * 100, 100) // 记录进度,取小,最大值100
current += size
}
}
如果上传到一半被中断了咋办?我们前端可以把每次的current存储在localStorage里面,网络恢复了,接续按照当前进度上传。
总结下切片上传的好处:
- 可上传大文件,且性能能被考虑到
- 能够知晓真实的进度
- 能够支持断点续传
blob数据转成base64
我们可以借助FileReader对象,将一些量较小的二进制数据转成base64,例如切片数据。
// 将切片数据转成base64,可以借助FileReader对象
let fr = new FileReader()
fr.readAsDataURL(sliceBlobFile) // 调用api转换,但是是个异步操作,需要我们去监听
fr.onload = () => { // 异步监听
let res = fr.result
// 可以直接给img回显
}
// 这个对象还可以转换成文本信息
let frText = new FileReader()
frText.readAsText(sliceBlobFile) // 调用api转换,但是是个异步操作,需要我们去监听
frText.onload = () => { // 异步监听
let res = fr.result
// 可以直接放在标签中回显
}