前端文件相关操作

221 阅读4分钟

一. 文件下载

(1) blob对象(以blob对象接收文件流)

  • 模板文件下载: 以vue项目为例,模板文件放置public目录下,
axios({
    url: 'xxxxx', // URL, 根据实际情况来 , 一般为‘应用包部署的baseUrl(即publicPath) + 模板文件路径’
    method: "get",
    responseType: "blob"
})
// 使用a标签承载下载功能
const blob = new Blob([data], { type: 'xxxxx' });
if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, filename);
} else {
    const href = URL.createObjectURL(blob); // 创建新的URL表示指定的blob对象(为生成的 URL 存储了一个 URL → Blob 映射)
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = href; 
    a.download = filename;
    a.click();
    URL.revokeObjectURL(a.href); // 释放URL对象, 防止内存泄漏
}

(2) a标签download(后端直接返回url地址)

// 文件下载, 文件名展示,a标签download下载
<a :download="name" :href="url">  // 不支持跨域链接

// 图片下载(也适用于抓图场景)
// a标签download下载的内容浏览器可以识别时(img, .pdf等),会直接预览的效果,需转换为blob对象阻止浏览器直接解析
fetch(url).then((res) => res.blob()).then((blob) => {
    // 使用a标签承载下载功能
    // ...
})

(3) Base64下载

类似于blob下载

// 使用filReader + readAsDataURL, 读取为base64编码后的`url`形式

if (xxxx) { 
    const fileReader = new FileReader();
    fileReader.readAsDataURL(respObj);
    fileReader.onload = function () {
        const a = document.createElement('a');
        a.style.display = 'none';
        a.href = this.result;
        a.download = name;
        document.body.appendChild(a);
        a.click(); document.body.removeChild(a);
    };
}

(4) window.open/location.href

二. 文件上传, 预览

  • 普通上传

(1) formData上传 + blob对象预览: 不兼容h5

// 上传
const fd = new FormData();
// ...
axios({
    url: "xxxxxx",
    method: "post",
    headers: { "Content-Type": "multipart/form-data" },
    data: fd
});
// 使用对象url进行预览, 格式为“blob:<origin>/<uuid>”, 即blob协议url
const url = URL.createObjectURL(data);
<a :download="name" :href="url">

(2) base64上传 + Data Url预览: 使用filReader+readAsDataURL,兼容h5

// 上传
let fr = new FileReader();
fr.onload = function (e) {
    that.imgBaseSrc = e.target.result;
    //上传后台
    // ...
};
// 一般图片使用Data Url形式读取(fr.readAsDataURL), 文件使用二进制数据流形式读取(fr.readAsBinaryString)
fr.readAsDataURL(file.files[0]);  

/**
 * 预览, Web性能优化有一项措施:把小图片用base64编码直接嵌入到HTML文件中,实际就是利用
 * 了Data URL来获取图片数据
 */
src格式为:‘data:image/jpeg:base64,xxxxxx’
<img :src="imgBaseSrc">

Data Url和Blob Url的区别

  1. 前者用于小图片展示,后者用于大图片展示
  2. 前者移植性较好,可以在任何浏览器中使用,后者只能在当前应用使用
  3. Blob URL可以方便的使用xhr对象获取源数据

文件超大上传问题: 切片上传结合断点续传

核心点:切片上传、断点续传、进度管控

核心思路:

1.生成文件的唯一标识(获取已上传的切片数据)

2.将file对象进行切片, 分段进行上传操作

3.上传过程中进行进度管控(基于xhr.upload.onprogress事件)

详细代码参考: js管理大文件上传及断点续传解析


注意:实际开发过程中服务端会限制上传的文件大小(例如20M), 前端再上传之前会进行压缩、加水印等操作

// watermark + Compressor + fetch
watermark([img])
    .image(xxx)
    .then((imgRes) => fetch(imgRes.src))
    .then((resp) => resp.blob())
    .then((blob) => {
      // 压缩
      const fileWM = new File([blob], xxx);
      return new Compressor(fileWM, {
        quality: 0.8,
        maxWidth: 1000,
        maxHeight: 1000,
        convertTypes: ['image/png', 'image/jpeg'],
        convertSize: 50000,
        ,,,
      });
    });


三. 数据批量导入

以导入excel数据为例 核心思路:以二进制数据流形式读取,将二进制文件流解析为xlsx对象,读取excel工作表数据转化为json数据 使用技术点:XLSX, readAsBinaryString, FileReader

let fr = new FileReader();
fr.onload = function (e) {
    const data = e.target.result;
    const wb = XLSX.read(data, { type: 'binary' });
    // 读取工作表数据转化为json数据
    xxxxxx
};
fr.readAsBinaryString(file.files[0]);

// readAsBinaryString, 按字节读取,读取二进制数据并编码为字符串
// readAsArrayBuffer, 按字节读取,读取二进制数据为ArrayBuffer对象

四. 数据批量导出

类似文件下载功能:blob对象实现即时下载

五. 页面截图

核心思路:

  • 使用 canvas.drawImage 在 canvas 上绘制图像
  • 调用 canvas 方法 .toBlob创建一个 Blob,实现即时下载 也可以借助 html2canvas 库实现

六. blob与base64转换

// blob -> base64
/**
 * 借助FileReader实现转化
 * return base64Url
 */
function blobToDataURL(blob) {
    let a = new FileReader();
    a.onload = function (e) {
        return e.target.result; 
    }
    a.readAsDataURL(blob);
}

// base64 -> blob
/**
 * 将以base64的图片url数据转换为Blob
 */
function convertBase64UrlToBlob(urlData){
    const arr = urlData.split(',');
    const mime = arr[0].match(/:(.*?);/)[1];
    const bstr = atob(arr[1]);
    const n = bstr.length;
    const u8arr = new Uint8Array(n);
    while(n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {type:mime});
}

二进制数据、blob、typedArray的相关说明

  • BufferSource 是 ArrayBuffer 或 ArrayBufferView(视图,指代typedArray或者DataView) 的总称
  • arrayBuffer,Uint8Array 及其他 BufferSource 是“二进制数据”,而 Blob 表示“具有类型的二进制数据”
  • Blob 和低级别的二进制数据类型之间进行转换: 使用 new Blob(...) 构造函数从一个类型化数组(typed array)创建 Blob。 使用 FileReader 从 Blob 中取回 arrayBuffer(readAsArrayBuffer),然后在其上创建一个视图(view),用于二进制处理。
  • 处理大型blob数据流, 转换为stream(blob.stream()),用于分段读取;或者使用异步generator
// 从 blob 获取可读流readableStream
const readableStream = blob.stream(); 
// 或者通过fetch(response.body)提供可读流对象
// fetch('https://www.example.org')
//  .then((response) => response.body)
//  .then((rb) => {
//    const reader = rb.getReader();
//  };

const reader = readableStream.getReader();
while (true) {
    // 对于每次迭代:value 是下一个 blob 数据片段
    let { done, value } = await reader.read();
    if (done) {
        // 读取完毕,stream 里已经没有数据了
        console.log('all blob processed.');
        break;
    }
    // 对刚从 blob 中读取的数据片段做一些处理
    console.log(value);
}

参考链接: ArrayBuffer与类型化数组流对象Stream