一、前言
还以为前端只能写页面、调接口、撸样式?其实在你不经意之间,我们早已悄悄摸上了“文件操作”的高地。从用户拖进来的一个 Excel 表处理,到实现分片上传、断点续传、预览下载……前端工程师正在变得越来越像“全能选手”。
这一篇文章不讲概念套话,带你边学边做,手把手搞定文件处理的各种“骚操作”。对新手小白友好,读完它,你将收获一份“文件操作锦囊”。
二、文件操作原理
在开始实现文件操作之前,我们需要了解几个基本的文件相关对象:
File:通过input标签读过来的文件对象
Blob:是类文件对象,用来表示不可变的原始数据,包含很多操作方法
FileReader:文件读取器,将文件读取为某种格式(如 base64、ArrayBuffer、Text)。
FormData:是专门用于和后端交互的对象,允许将 File 对象等数据打包成表单格式发送给后端。
1.File、Blob对象
File是Blob的子类,具有Blob的所有功能,因此我们通常不需要专门使用Blob
在实际开发中,我们一般不会直接 new 一个 File或 Blob 对象
可以直接通过 input 标签选择文件的change事件中获取到 File 对象。
比如:
// 监听change事件
<input type="file" name="file" @change="fileChange" multiple />
// 通过e.target.files获取到选取的文件对象数组
function fileChange(e) {
fileObj = e.target.files[0] // 获取选择的第一个文件,这时候得到的就是一个文件对象
}
2.FileReader文件读取器
FileReader通常只有在使用第三方库或者要对文件进行特殊处理时使用
一般情况下直接使用File对象就可以了
需要注意的是,FileReader的读取操作是异步的、需要在onload方法中获取结果
我们可以定义FileReader的onload方法,等待它执行完毕后获取到处理后的文件数据
可以利用 Promise+await 确保操作顺序
比如:
const handle = async (e) => {
let file = e.raw// 从el-table中获取选中数据,是一个file对象
if (!file) return
let data = await readFile(file)// 将文件放入定义的读取方法中读取,并使用await等待结果
}
function readFile(file) {
return new Promise((resolve) => {// 使用Promise管理异步操作
let reader = new FileReader()// 创建一个FileReader实例
reader.readAsArrayBuffer(file)// 将文件读取为ArrayBuffer格式
reader.onload = (e) => {// 因为读取是异步的,所以在onload方法中获取
resolve(e.target.result)// 返回处理结果
}
})
}
3.FormData
首先我们要明白,前端的File对象后端是无法直接识别的
这时候就需要FormData挂载File对象传给后端
也可以使用FileReader文件读取器将File对象读取为base64、text格式传给后端
(ps:读取为base64格式时可以做预览图效果,直接放入img的scr中替换路径即可)
比如:
示例1
// 创建一个新的 FormData 对象
const formData = new FormData();
// 将文件和其他数据添加到 FormData 对象
formData.append("file", fileInput.files[0]); // 添加文件
formData.append("username", usernameInput.value); // 添加用户名
// 上传文件时直接把formData传过去就可以了
cosnt res = await uploadFileAPI(formData)
示例2
// 如果想将已经定义好了的对象转为FormData,可以结合遍历方法实现
// 注意转化只是一个比喻,实际上还是通过append修改FormData实例
const data = {
name: 'John Doe',
email: 'john.doe@example.com',
age: 30
};
// 创建 FormData 对象
const formData = new FormData();
// 使用 Object.entries 遍历对象并将每个键值对添加到 FormData
Object.entries(data).forEach(([key, value]) => {
formData.append(key, value);
});
// 现在你可以使用 formData 进行表单提交了
cosnt res = await uploadFileAPI(formData)
三、完整代码演示(分片上传)
接下来,让我们用 Vue 3 来实现一个简单的文件上传和分片上传的示例。
<script setup>
import { ref } from 'vue'
// 存储图像的base64编码,用于显示预览
const imgbase64 = ref()
// 存储文件对象
let fileObj
// 存储上传进度
let percentage = ref(0)
// 文件选择变化时触发,获取文件对象
function fileChange(e) {
fileObj = e.target.files[0] // 获取选择的第一个文件
}
// 提交文件的函数
const submit = async () => {
// 设置每个分片的大小为2MB
let size = 2 * 1024 * 1024
let current = 0 // 当前上传的位置
// 分片上传文件
const post = () => {
if (current > fileObj.size) return // 如果上传的字节数超过文件总大小,停止上传
// 创建 FormData 对象,用于将文件分片添加到请求中
const _formData = new FormData()
const data = fileObj.slice(
Math.max(0, current), // 取当前分片的起始位置
Math.min(current + size, fileObj.size) // 取当前分片的结束位置
)
_formData.append(fileObj.name, data) // 将分片添加到 FormData
// 使用 setTimeout 模拟异步请求,真实情况可用 async/await 和 fetch 代替
setTimeout(() => {
current += size // 更新当前上传位置
percentage.value = Math.min((current / fileObj.size) * 100, 100).toFixed(2) // 计算上传进度并显示
console.log(percentage.value) // 打印上传进度
post() // 递归上传下一个分片
}, 10) // 模拟延迟上传
}
post() // 调用 post 函数开始上传
}
</script>
<template>
<div>
<!-- 文件选择框,绑定 fileChange 方法 -->
<input type="file" name="file" @change="fileChange" multiple />
<!-- 图像预览(如果选择了图片文件) -->
<img :src="imgbase64" />
<!-- 显示上传进度 -->
<br />
<div>进度 {{ percentage }}%</div>
<!-- 提交按钮,点击后触发 submit 方法 -->
<br />
<el-button @click="submit">提交</el-button>
</div>
</template>
<style lang="scss" scoped>
img {
width: 200px; // 设置预览图像的宽度
height: 200px; // 设置预览图像的高度
}
</style>
四、结语
希望这篇文章能帮助大家理解前端文件操作的原理,在实际开发中面对文件操作得心应手。
本次主要介绍了文件操作的基础原理,文件操作的世界远不止上传与预览。
在下一篇文章中,我们将深入探索前端如何处理 Excel 和 Word 文档
包括如何读取内容、格式转换、导出下载等实用技巧。
不管你是要做数据报表、合同生成,还是在线文档预览,都是实打实的刚需技能。
敬请期待,下一站——玩转Excel、Word!