文件上传基本使用认知
前端上传文件传给后端两种形式:二进制blob base64
相关对象
- files:通过<input type="file"/ >读取的文件对象,属于blob(不可改)
- blob: 不可变的二进制内容,包含很多操作方法
- formData:前端通过input选择的files是属于前端的对象,后端是拿不到的,这时候formData就充当前后端都认识的载体来传递文件
- fileReader:把文件都取成某种形式,如base64,text文本
- 我们的文件上传和断点上传都是基于blob的
- 二进制blob传输是formData承载着files对象进行的传输
files
首先我们来感受一下files是什么,我们通过input上传:
<script setup>
// 在这里编写组件逻辑
import { ref } from 'vue'
const fileChange = (e) => {
console.log(e.target.files)
}
</script>
<template>
<div>
<input type="file" @change="fileChange" />
</div>
</template>
便可以直接看到files的内容结构:这里我上传的是图片类型:
由于可能是上传多文件,这里我们拿到的是一个数组,具体的
files[0]、files[1]才是文件首要对象,在真实场景中肯定会做对用户文件上传类型和大小的限制,可以从files[0].type入手:
<script setup>
import { ref } from 'vue'
const fileChange = (e) => {
const file = e.target.files[0] // 获取单个文件对象
if (!file) return // 处理无文件上传的情况
// 检查文件大小(10MB限制)
if (file.size > 10 * 1024 * 1024) {
alert('文件大小不能超过10MB')
e.target.value = '' // 清空文件选择
return
}
// 检查文件类型
if (!['image/jpeg', 'image/png', 'video/mp4'].includes(file.type)) {
alert('文件类型必须是JPEG、PNG或MP4')
e.target.value = '' // 清空文件选择
return
}
// 处理MP4文件的特殊逻辑
if (file.type === 'video/mp4') {
console.log('上传了MP4文件:', file)
} else {
console.log('上传了图片:', file)
}
}
</script>
<template>
<div>
<input type="file" @change="fileChange" />
</div>
</template>
这样就很好地限制了文件的类型、大小等
可以自己new一个对象来创建file文件:
new File('[Jice19]','a.txt') //创建了一个a.txt文本文件,第一个对象是数组
file与blob的联系
刚才我们了解到file文件属于blob,那么自然能够实现相互转换,
- 实现了文件转换成blob:
console.log(new Blob([file])) //第一个对象必须是数组
2.实现了blob转换成files:需要了解blob可以把文件进行切片,这里我们做简单实现,后面详细讲解
let sliceBlob = new Blob([file]).slice(0,100) //切掉file的前半部分创建新对象
let sliceFile = new File([sliceBlob]) //将切割后的file对象上传上去
fileReader
作用:将blob和file对象转换成Base64或文字格式
注意:由于读取是异步的,我们不能直接拿到结果,我们需要监听读取的onload事件获取到转化结果
//文件读取示例
let sliceBlob = new Blob([file]).slice(0,100) //切掉file的前半部分创建新对象
let sliceFile = new File([sliceBlob]) //将切割后的file对象上传上去
let fr = new FileReader()
fr.readAsDataURL(sliceFile) //将切片后的文件转换成base64
fr.onload=function(){
console.log(fr.result) //只有在onload监听时才能拿到结果
}
通过读取同一张文件的截取之前之后(file /sliceFile)可以很明显地看出切片效果:
实战:缩略图的实现、用户上传文件文本预览
我们想要预览用户上传的图片文件,只需要动态绑定上通过Filereader读取的、只在onload这一事件时得到的文件,即可实现预览:
<script setup>
import { ref } from 'vue'
const imgBase64 = ref('')
const isLoading = ref(false)
const errorMsg = ref('')
const fileChange = (e) => {
const file = e.target.files[0]
if (!file) return
// 检查文件类型是否为图片
if (!file.type.startsWith('image/')) {
errorMsg.value = '请上传图片文件'
return
}
isLoading.value = true
errorMsg.value = ''
const reader = new FileReader()
reader.onload = (event) => {
imgBase64.value = event.target.result
isLoading.value = false
}
reader.onerror = () => {
errorMsg.value = '图片读取失败'
isLoading.value = false
}
//读取到文件转换成base64然后绑定给img实现用户文件预览
reader.readAsDataURL(file)
}
</script>
<template>
<div>
<input type="file" @change="fileChange" />
<div v-if="errorMsg">{{ errorMsg }}</div>
<div v-else-if="isLoading">
图片加载中...
</div>
<div v-else-if="imgBase64">
<img style='height:200px;width:200px' :src="imgBase64" alt="用户上传的图片" />
</div>
</div>
</template>
(注:文本文件也是同理,通过reader.readAsText实现预览)
文件传送给后端
我们刚才了解到通过formData将数据传递给后端,先new一个formData对象,我们读取到文件之后,直接把文件给到对象,然后通过formData对象的append方法搭载数据,最后直接把对象传递给接口就好
//实现思路
<template>
<form method="post">
<input type="text" @change="fileChange" />
</form>
<button @click="submit">提交</button>
</template>
<script setup>
async submit(){
let formData = new FormData()
formData.append('file1',file1)
formData.append('file2',file2)
axios.post('/xx',formData)
}
</script>