文件的导入,导出,预览,以及文件相关api的基本介绍
前端项目中的导入导出功能必不可少
导入功能可以基于input type = 'file' 实现
导入包括选择文件与调接口上传文件流
介绍几种文件的相关api
File
首先,我们可以用通过input type=file 得到文件流对象 也就是e.targrt.files[0]
在e.target中,files是一个数组,由于我们选择文件默认是只能选择一个数据,所以我们就取数组第0项得到文件流File对象
得到的文件流对象就是这种样式
File {name: 'heima.png', lastModified: 1568160757235, lastModifiedDate: Wed Sep 11 2019 08:12:37 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 5965, …}
lastModified: 1568160757235
lastModifiedDate: Wed Sep 11 2019 08:12:37 GMT+0800 (中国标准时间) {}
name: "heima.png" // 文件名
size: 5965 // 文件大小,单位是字节kb
type: "image/png" // 文件类型
webkitRelativePath: ""
[[Prototype]]: File
除了使用input标签获取一个File对象以外我们还可以直接new 出一个对象,不过这种东西我们一般不用
const files = new File(['第一个参数只一个数组,里面存放的是文件内容'], '第二个参数是文件名称.txt');
console.log(files);
File {name: '第二个参数是文件名称.txt', lastModified: 1688175657382, lastModifiedDate: Sat Jul 01 2023 09:40:57 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 63, …}
lastModified: 1688175657382
lastModifiedDate: Sat Jul 01 2023 09:40:57 GMT+0800 (中国标准时间) {}
name: "第二个参数是文件名称.txt"
size: 63
type: ""
webkitRelativePath: ""
[[Prototype]]: File
Blob
File 对象是 Blob 的一个子类对象,Blob(一个不变的二进制对象) 二者可以相互转化
将File对象转化为一个Blob对象
const files = new File(['第一个参数只一个数组,里面存放的是文件内容'], '第二个参数是文件名称.txt');
const blob = new Blob([files]);
console.log(blob);
Blob {size: 63, type: ''}
size: 63
type: ""
将Blob对象转化为File对象
const BlobToFile = () => {
const blob = new Blob(); // 新建一个Blob 对象
console.log(blob);
const _file = new File([blob], 'test.js'); // 将Blob 对象转化为 File 对象,接受两个参数第一个是将要接受转化的Blob对象,第二个是文件名
console.log(_file);
}
FileReader
这个对象主要可以将文件流对象(File),二进制对象(Blob)转化为任何我们可以想要的格式,包括base64 格式,txt 格式等
const getfileObject = (e) => {
let _files = e.target.files[0];
let blobs = new Blob([_files]);
let spliceBlobs = blobs.slice(0, 5000); // 将blob对象分割
// console.log(spliceBlobs); // 切割后的blob对象
const fr = new FileReader(); // 文件读取工具函数
// fr.readAsArrayBuffer(spliceBlobs); // 将blob对象读取为一个arrayBuffer 对象
// fr.readAsDataURL(spliceBlobs); // 读取为base64
fr.readAsText(spliceBlobs) // 读取为文本,这里就可以实现读取本地的文件
fr.onload = () => { // 读取文件是个异步的任务,我们只能在onload 监听函数里面得到这个读取结果
// console.log(fr.result);
setImgUrl(fr.result);
}
}
FormData
我们实现文件上传主要就是使用formdata实例包裹文件流对象/二进制对象,或者上传base64
let _formdata = new FromData();
_formdata.append('file',files);
axios.post(...)
单文件上传
const getFileObject = (e) => {
let file = e.target.files[0];
let _formData = new FormData();
_formData.append('后端要求的文件名称,这里使用file'._formData);
// 调用接口传递数据
axios({
url:'xxxxxxx',
method:'POST',
data:_formData
}).then((res) => {
coonsole.log(res)
}).catch((error)=> {
console.error(error)
})
}
多文件上传
我们可以通过input的 multiple 属性实现多文件选择
这样我们在选择文件的时候就可以按住ctrl键实现多选
但是这样是不符合我们用户习惯的,所以我们可以做一个完全控制的文件上传
// 每次触发change事件都向文件数组里面push一个文件流对象,不用input的multiple属性
const getfileObject = (e) => {
let files = e.target.files[0];
let newFilesList = filesList;
newFilesList.push({ id: filesList.length + 1, fileName: files.name, fileObj: files });
setFilesList(newFilesList);
console.log(newFilesList);
// 自己渲染一个新的已经选择的文件列表,如果想删除可以使用id查找然后在数组里面删除
}
切片上传
const oneByOneUpload = async () => {
let fileOgj = filesList; // 单个文件的文件流对象;
let size=2*1024*1024;
let current = 0;
let fileSize = fileOgj.size;
while (current < fileSize) {
let _formdata = new FormData();
_formdata.append('filename',fileObj.slice(current,current + size))
const {data : res} = await axios({
url:'请求地址',
method:'POST',
data:_formdata
})
if(res && res.code === 200){// 接口请求成功
current = current + size;
}
}
}
文件的下载与预览
文件预览
| 通用 | vue | react |
|---|---|---|
| xlsx | @vue-office/excel | react-file-viewer |
xlsx工作流程(产物转化示意图)
blob或本地读取的文件流对象 => 转化为 arrayBuffer => read方法读取文book对象,(就是一个execl对象) => 提取出对应的sheet对象
sheet对象 <==> html(table样式的dom元素)或 json 数组样式的数据
sheet对象 ==> 创建wookbook ==> 输出为excel文件 (也就是文件下载了);
我们可以选择本地文件或者请求的文件对象实现在本地预览
import { read, utils, writeFile } from 'xlsx';
/**
* 自己选择本地文件、或请求后端数据 实现浏览器预览
* 注意如果后端请求数据的时候要加上{responseType:'blob'}
*/
const getfileObject = async (e) => {
let _file = e.target.files[0]; // 这里是本地选择的文件,我们也可以从后端请求数据
// 1,调用文件流对象上的arrayBuffer方法将文件流对象转化为arrayBuffer对象,这个方法返回一个promise 对象
const res = await _file.arrayBuffer();
console.log(res); // arrayBuffer 对象
// 2.调用read方法将arrayBuffer 对象转化为 wookbook 对象
const wb = read(res); // 这就可以得到一个execl文件对象,里面可能有多个sheet 对象
console.log(wb); // wookbook 对象
// 3,得到文件里面的一个sheet对象
const sheet1 = wb.Sheets.Sheet1
console.log(sheet1); // 工作表sheet对象
const _data = utils.sheet_to_json(sheet1); // 常用的是sheet_to_json sheet_to_html
console.log(_data); // 得到一个数组
const _html = utils.sheet_to_html(sheet1); // 生成一个没有任何样式的table表格数据
setHtml(_html);
}
xlsx可以实现将文件流对象转化为前端数据或dom元素,也可以将前端数据或者dom元素转化为excel对象,可就是可以实现前端的文件下载功能
不依赖后端的数据导出
/**
* 不依赖后端的数据导出
*/
const exportWithoutBack = () => {
// 数据转化为 excel 文件
let data = [
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' },
{ '姓名': 'xxx', '年龄': '18', '性别': '男' }
];
const ws = utils.json_to_sheet(data); // 将数据转化为一个sheet工作表,返回一个wookSheet对象
const wb = utils.book_new(); // 新建一个execl wookbook对象,因为我们下载是下载的excel对象而不是sheet对象
utils.book_append_sheet(wb, ws, '工作表');
writeFile(wb, '导出的文件.xlsx');
// table dom 转化为excel 导出
// const wb2 = utils.table_to_book(domRef); // 前端table dom 可以直接转化为一个wookbook 对象,当然我们也可以按部就班的将dom 先转化为sheet 再新建一个wookbook ,将sheet加入到wookbook 中,最终将wookbook 用writeFile()导出
// writeFile(we2);
}
文件下载
方式一:直接访问文件的内存地址可以直接在浏览器里面下载(显然这种方式不对),无法添加token等限制
window.open('https://文件地址')
方式二:利用a标签
流程:
2.1 请求接口数据,注意必须接受二进制类型的数据
2.2 兼容ie浏览器,判断是否有msSaveBlob,若有则使用msSaveBlob下载
2.3 若不是ie浏览器,则支持a标签下载
2.3.1,利用 URL.createObjectURL() 创建本地文件url
2.3.2,创建a标签
2.3.3,将创建的url给a标签的href赋值
2.3.4,设置download
2.3.5,模拟a标签的点击事件
2.3.6,下载完成
const downnloadFile = async () => {
const {data: res} = await axios({
url:'xxxxx',
method:'POST',
headers:{
responseType:'blob'
},
});
console.log(res); // 文件流对象
if(window.navigator.msSaveBolb){
// ie
window.navigator.msSaveBolb(res,{type:'文件类型'});
}else {
const url = URL.createObjectURL(res); // 将请求的blob对象转换为一个本地的url
let aLink = document.createElement('a'); // 创建a标签
aLink.href = url; // 将创建的本地文件地址url 赋值给a标签的href 属性
aLink.download='文件名.xslx'; // 设置下载文件的文件名
aLink.style.displsy = 'none'; // 隐藏创建的a标签
aLink.click(); // 触发点击事件
URL.revokeObjectURL(url); // 销毁创建的本地文件url
}
}
方式三:file-saver
import {saveAs} from 'file-saver';
const downLoadFile = async () => {
const {data : res} = await axios({
url:'xxxxx',
method:'POST',
headers:{
responseType:'blob',
}
});
saveAs(res,'文件名.xlsx');
}
在input type="file" 中遇到的坑
注意:在我们上传文件的时候,如果前后选则的文件是同一个文件,(可能文件编辑之后重新上传),那么input 的onchange事件不会重新触发。
解决方法:重新选择文件上传的时候将value置空
<input type="file" @change="getFileObject" @click="(e) => {
e.target.value = null;
}" />