Blob实现文件上传,下载与压缩

3,066 阅读2分钟

定义: Blob对象表示不可变的,包含原始数据的类文件对象。

  • 两个参数
    • 数据类型的数据对象(ArrayBuffer,ArraybufferView,Blob,String等)
    • MINE类型
    • 结果:产生一个Blob对象

一、文件下载

1.1、a标签 href下载

浏览器不识别响应头,或者不能解析的格式,会当成文件直接下载。

1.2、base64

将文件转成base64后,作为a,img标签的src,css的url()

1.3、Blob流

注释: 1.ArrayBuffer接收读取文件的二进制流格式数据

2.window.URL.createObjectURL(blob);将Blob流转换成DOMString


个人使用: ==使用a标签的download下载(FF不支持download属性)==

window.open,个别浏览器能识别个别图片图片格式,是预览,而非下载

// 请求文件数据
axios({
  method: 'GET',
  url: record.url,
  responseType: 'blob'
}).then(res=>{
    if(!res){
        this.$message.warning('文件下载失败');
        return
    }
  // 创建Blob对应格式的Blob对象
  let blob = new Blob([res.data], {type: mimeType});
  
  // 检测是否兼容download属性(FF不支持download属性)
  if('download' in document.createElement("a")){
    const link = document.createElement("a");
    link.href = window.URL.createObjectURL(blob);
    link.download = record.name;
    link.click();
    link.remove();
    window.URL.revokeObjectURL(link.href);
  }else{
    // 缺点:个别浏览器能识别个别图片格式,会直接浏览,而非下载
    let targetUrl = window.URL.createObjectURL(blob);
    window.open(targetUrl);
    window.URL.revokeObjectURL(targetUrl);
  }
}).catch(err=>{
  console.error(err)
})

问题:兼容搜狗浏览器,但是360浏览器上window.open打开的是标签页,也不是窗口

解决方案:使用iframe(测试:无法修改文件名称)

function IEdownloadFile(fileName, contentOrPath){
 var ifr = document.createElement('iframe');
 ifr.style.display = 'none';
 ifr.src = contentOrPath;
 document.body.appendChild(ifr);
 // 保存页面 -> 保存文件
 ifr.contentWindow.document.execCommand('SaveAs', false, fileName);
 document.body.removeChild(ifr);
}

// base64 编码文件
var isImg = contentOrPath.slice(0, 10) === "data:image";

// dataURL 的情况
isImg && ifr.contentWindow.document.write("<img src='" + 
  contentOrPath + "' />");

二、压缩

  • 文件流转Base64
  • 图片有损压缩,toDataURL
/*
* 文件流转Base64
* @param {file} file 图片对象
* @param {number} quality 图片质量 范围: [0,1] 超出默认为0.92
* */
fileToBase64ByQuality(file,quality){
    return new Promise((resolve, reject) => {
        if(file.type === 'image/jpeg' || file.type === 'image/webp'){
          let fileReader = new FileReader()
          let type = file.type
            if (window.URL || window.webkitURL) {
              resolve(this.fileCompress(URL.createObjectURL(file), quality, type, file.name))
            } else {
              fileReader.onload = () => {
                resolve(this.fileCompress(fileReader.result, quality, type, file.name))
              }
              fileReader.onerror = (e) => {
                reject(e)
              }
              fileReader.readAsDataURL(file)
            }
          
        }else{
            resolve(file);
        }
    })
},
  • 文件压缩
/*
  * 规则:只能是在格式为image/jpeg,和image/webp得情况下才能用
  * @param {file} fileBase64 图片base64数据
  * */
  fileCompress(fileBase64, quality, mimeType, fileName){
    //  图片最大宽度
    const MAX_WIDTH = 800
    let cvs = document.createElement('canvas');
    let img = document.createElement('img');
    img.crossOrigin = 'anonymous'
    return new Promise((resolve, reject) => {
      img.src = fileBase64
      // 图片偏移值
      let offetX = 0
      img.onload = () => {
        if (img.width > MAX_WIDTH) {
          cvs.width = MAX_WIDTH
          cvs.height = img.height * MAX_WIDTH / img.width
          offetX = (img.width - MAX_WIDTH) / 2
        } else {
          cvs.width = img.width
          cvs.height = img.height
        }
        let ctx = cvs.getContext("2d").drawImage(img, 0, 0, cvs.width, cvs.height)
        let imageData = cvs.toDataURL(mimeType, quality)
        resolve(this.convertBase64UrlToBlob(imageData, mimeType, fileName))
      }
    })
  },
  • base64转文件流
 /**
* base64转文件流
* @param {base64} base64数据
* @param {string} mimeType MIME类型
* @return {file}  文件blob二进制数据
*/
convertBase64UrlToBlob(base64, mimeType, fileName){
    let bytes = window.atob(base64.split(',')[1])
    let ab = new ArrayBuffer(bytes.length)
    let ia = new Uint8Array(ab)
    for (let i = 0; i < bytes.length; i++) {
      ia[i] = bytes.charCodeAt(i)
    }
    //base64转成Blob
    let _blob = new Blob([ab], { type: mimeType })
    let files = new File([_blob], fileName, {type: mimeType})
    return files
},

大文件分片上传

Blob对象有个slice方法,可将大文件按相同大小切割成n-1个,加上最后一节

function fileSlice(file, piece = 1024 * 1024){
	let allSize = file.size;
    let start = 0;
    let end = start + piece;
    let filePieceArr = [];
    while(end > allSize){
    	filePieceArr.push(file.slice(start, end));
        start = end;
        end = start + piece;
    }
    return filePieceArr
}

刚开始写文章,可以的话,麻烦给点建议,谢谢