前端文件上传与相关操作
前置知识
上传方案
- 通过二进制
blob传输,如通过formData传输 - 通过转为
base64字符串上传,但字符串体积会较大
相关对象
files
用户选择一个或多个文件后,可通过 <input type="file"> 元素获取文件数据,返回 FileList 对象,包含用户选择的文件。可获取文件类型、名称、大小等信息,前端用于做其他处理。
const fileInput = document.getElementById('fileInput')
fileInput.addEventListener('change', (event) => {
const selectedFiles = event.target.files
// 遍历选中的文件
for (let i = 0; i < selectedFiles.length; i++) {
const file = selectedFiles[i]
console.log('文件名:', file.name)
console.log('文件类型:', file.type)
console.log('文件大小:', file.size, 'bytes')
if (file.type !== 'video/mp4') {
alert('只能上传mp4文件')
}
}
})其中,file 打印结果如下所示:

拓展
file 对象本质是 File 类的实例化。这是另一种创建文件的方式,但一般不这么做,因为无法让用户自己选择,也无法写入磁盘。
console.log(new File(['content'], 'a.txt')) // 第一个参数必须是数组,第二个参数是文件名打印结果如下所示:

注意
- 获取到的对象不能直接给后端,后端不认识前端对象,需使用
formData转换 file对象是blob对象的子类,本质也是blob
blob
Blob(Binary Large Object)是一种数据对象,用于表示二进制数据。前端上传文件给后端时,通常将文件数据转为 Blob 对象进行传输。使用 Blob 构造函数创建 Blob 对象,接受一个参数 blobParts,包含要构建 Blob 的数据。blobParts 可以是数组,每个元素代表一部分数据,也可以是包含数据的字符串。
file对象放进new Blob()方法内即可转为blob对象。
blob 对象可使用 slice 进行切割操作,放进 new File() 方法内即可转为 file 对象。将切割后的 blob 放进去可转为新的 file 对象。步骤如下:
- 获取文件并转为
blob对象 - 通过
slice()方法截取 - 通过
new File()将截取后的blob转为files对象
const fileInput = document.getElementById('fileInput')
fileInput.addEventListener('change', () => {
const selectedFile = fileInput.files[0]
// 将文件转换为 Blob 对象
const blob = new Blob([selectedFile], { type: selectedFile.type })
// 执行其他操作,例如将 Blob 对象上传到服务器
})<input type="file" @change="change" />
<script>
const change = (e) => {
let file = e.target.files[0]
const blob = new Blob([file])
const sliceBlob = new Blob([file]).slice(0, 5000) // 切割0到5000位
const sliceFile = new File([sliceBlob], 'text.png')
console.log(sliceFile)
}
</script>最后可以把切割好的 sliceFile 调用接口传给后端。
注意
后端直接给前端的数据前端也不认识,需要转换。
formData
FormData 是用于构建和处理表单数据的 JavaScript 对象。可将表单数据格式化为可通过 XMLHttpRequest 或 Fetch API 发送到服务器的数据体。
使用 FormData 可处理包括文本字段、文件和二进制数据在内的表单数据。搭载 files 对象传递给后端。本质是将前端的 file 对象转为 blob 传递给后端。
const change = (e) => {
const formData = new FormData()
formData.append('user', 'content')
formData.append('file', e.target.file[0])
axios.post('/xx', formData)
}最终查看接口,传递的参数是二进制文件,请求头也是相应的 form-data 类型。
注意
使用 FormData 对象可方便地处理带有文件和二进制数据的表单,但对于只包含文本数据的简单表单,也可直接将数据格式化为 JSON 对象或查询字符串发送到服务器。具体处理方式取决于服务器端要求和所使用的前端框架或库。
fileReader
FileReader 是用于读取文件内容的 JavaScript 对象。提供了异步读取文件数据的方法,并可按文本、二进制数据、base64 或数据 URL 等不同方式处理。
const fileInput = document.getElementById('fileInput')
fileInput.addEventListener('change', (event) => {
const selectedFile = event.target.files[0]
const reader = new FileReader()
reader.addEventListener('load', (event) => {
// 读取完成后的回调函数
const fileData = event.target.result
console.log(fileData)
// 在这里可以对文件数据进行进一步处理
})
// 将文件读取为文本
// reader.readAsText(selectedFile);
// 将文件读取为二进制数据
// reader.readAsArrayBuffer(selectedFile);
// 将文件读取为数据 URL
// reader.readAsDataURL(selectedFile);
// 将文件读取为指定长度的字符串
// reader.readAsBinaryString(selectedFile);
})| 方法 | 操作 |
|---|---|
readAsText() | 将文件读取为文本 |
readAsArrayBuffer() | 将文件读取为二进制数据 |
readAsDataURL() | 将文件读取为数据 URL |
readAsBinaryString() | 将文件读取为指定长度的字符串 |
每个方法都是异步的,操作完成时触发 load 事件,文件数据存储在 event.target.result 中。可根据需要选择合适的读取方法,在 load 事件处理函数中对数据做进一步处理。例如将文本显示在页面上、将二进制数据发送到服务器,或将数据 URL 用作图片的 src。
注意
读取大型文件或使用错误的读取方法可能影响性能或导致异常。确保选择正确的方法,并适当处理读取操作的成功或失败。
案例:缩略图
运用前置知识实现缩略图效果,步骤如下:
- 获取用户选择的
file文件 - 将
file转为blob格式,便于通过slice实现裁剪 - 裁剪后通过
new File()转为file对象 - 使用
fileReader的readAsDataURL()方法获取图片的base64格式,赋值给img标签的src属性
fileChange = (e) => {
const file = e.target.files[0]
const blob = new Blob([file])
const sliceBlob = new Blob([file]).slice(0, 5000) // 切割0到5000位
const sliceFile = new File([sliceBlob], 'text.png')
const reader = new FileReader()
reader.readAsDataURL(sliceFile)
reader.onload = (e) => {
this.imgSrc = e.target.result
}
}<template>
<input type="file" @change="fileChange" />
<img :src="imgSrc" alt="" />
</template>转换关系
转换关系如下:
files对象前端可直接获取,通过new Blob()转为blob,blob也可以通过new File()转为files对象。files是blob的子类files和blob都可用fileReader读取为文本和base64files和blob都要append到formdata内,调用接口传输

上传方式
单多文件上传
多文件上传就是遍历文件数组,一个个声明 formData 对象,调接口传参。(即循环单文件上传)
步骤如下:
- 每次用户选择文件后把文件保存到一个数组内
- 点击提交时遍历数组依次
append添加文件并各自提交
代码如下所示:
// 用户选择图片
const onChange = (e) => {
if (e.target.files.length > 1) {
imageList.value.concat(e.target.files)
} else {
imageList.value.push(e.target.files[0])
}
}
// 点击提交按钮
const onSubmit = async () => {
let _formData = new FormData()
imageList.value.forEach((item) => {
_formData.append(item.name + 'file', item)
axios.post('/xx', _formData)
})
}切片上传
思路:
设置一个最大值,定义一个变量 current 初始值为 0,上传文件时通过使用 slice(current, current + 设置的允许最大上传的尺寸) 上传文件,上传完后 current 加上当前上传的文件大小即可。
实现步骤如下:
- 获取用户选择的文件
- 点击提交按钮后获取文件的大小,初始化设置一个变量,保存切片上传的大小,默认为 0
while循环,只要上传切片的大小没有超过文件总大小,就调用接口传递参数,参数使用slice截取大小并累加已上传的文件大小- 通过
async和await把请求变为同步任务,保证上一个切片上传成功再上传下一个切片
代码如下所示:
<script setup>
const fileObj = ref({}) // 文件上传
const precent = ref(0) // 百分比
const onChange = (e) => {
fileObj.value = e.target.files[0]
}
const onSubmit = async () => {
let size = 2 * 1024 * 1024
let current = 0
let fileSize = fileObj.value.size
while (current < fileSize) {
let formData = new FormData()
formData.append(
fileObj.value.name,
fileObj.value.slice(current, current + size),
)
// 切片上传
await axios.post('/upload', formData)
// 累加文件大小
current += size
// 计算百分比
precent.value = Math.min((current / fileSize) * 100, 100)
}
}
</script>拓展
后端的做法是获取到切片的文件判断名称是否相同,相同则在后面做拼接的操作。
断点续传
思路:
当上传的时候中断了,例如 4mb,把中断的值本地存储,下次上传时从第 4mb 开始传起。
以上方代码示例为例:
<script setup>
const fileObj = ref({}) // 文件上传
const precent = ref(0) // 百分比
const onChange = (e) => {
fileObj.value = e.target.files[0]
}
const onSubmit = async () => {
if (!fileObj.value)
fileObj.value = JSON.parse(localStorage.getItem(fileObj.value.name)).file
let size = 2 * 1024 * 1024
let current = 0
let fileSize = fileObj.value.size
while (current < fileSize) {
let formData = new FormData()
formData.append(
fileObj.value.name,
fileObj.value.slice(current, current + size),
)
// 切片上传
const res = await axios.post('/upload', formData)
if (res.code === 200) {
// 累加文件大小
current += size
// 计算百分比
precent.value = Math.min((current / fileSize) * 100, 100)
} else {
// 存储当前文件信息
localStorage.setItem(
fileObj.value.name,
JSON.stringify({ file: fileObj.value, current }),
)
}
}
}
</script>