文件的导入,导出,预览,以及文件相关api的基本介绍

227 阅读7分钟

文件的导入,导出,预览,以及文件相关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;
      }
    }
  }

文件的下载与预览

文件预览

通用vuereact
xlsx@vue-office/excelreact-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;
    }" />