搜索
您的当前位置:首页正文

利用Blob进行文件上传的完整步骤

2020-11-27 来源:抵帆知识网

构造函数

创建blob对象本质上和创建一个其他对象的方式是一样的,都是使用Blob() 的构造函数来进行创建。 构造函数接受两个参数:

第一个参数为一个数据序列,格式可以是ArrayBuffer, ArrayBufferView, Blob, DOMString
第二个参数是一个包含以下两个属性的对象

  • type: MIME的类型,
  • endings: 决定第一个参数的数据格式。默认值为"transparent",用于指定包含行结束符n的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变。
  •  var data1 = "a";
     var blob1 = new Blob([data1]);
     console.log(blob1); //
    输出:Blob {size: 1, type: ""} var debug = {hello: "world"}; var blob = new Blob([JSON.stringify(debug, null, 2)],{type : 'application/json'}); console.log(blob) // 输出 Blob(22) {size: 22, type: "application/json"} // 创建一个8字节的ArrayBuffer,在其上创建一个每个数组元素为2字节的“视图” var abf = new ArrayBuffer(8) var abv = new Int16Array(abf) var bolb_ArrayBuffer = new Blob(abv, {type : 'text/plain'}) console.log(bolb_ArrayBuffer) //输出 Blob(4) {size: 4, type: "text/plain"}

    slice方法

    Blob对象有一个slice方法,返回一个新的 Blob对象,包含了源 Blob对象中指定范围内的数据。

    slice(start, end, contentType)

    start: 可选,代表 Blob 里的下标,表示第一个会被会被拷贝进新的 Blob 的字节的起始位置。如果传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。
    end: 可选,代表的是 Blob 的一个下标,这个下标-1的对应的字节将会是被拷贝进新的Blob 的最后一个字节。如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。
    contentType: 可选,给新的 Blob 赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。

    var data = "abcdef";
    var blob1 = new Blob([data]);
    var blob2 = blob1.slice(0,3);
    
    console.log(blob1); //
    输出:Blob {size: 6, type: ""} console.log(blob2); //输出:Blob {size: 3, type: ""}

    slice用于文件分片上传

  • 分片与并发结合,将一个大文件分割成多块,并发上传,极大地提高大文件的上传速度。
  • 当网络问题导致传输错误时,只需要重传出错分片,而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。
  • 分片上传逻辑如下:

    获取要上传文件的File对象,根据chunk(每片大小)对文件进行分片
    通过post方法轮循上传每片文件,其中url中拼接querystring用于描述当前上传的文件信息;post body中存放本次要上传的二进制数据片段
    接口每次返回offset,用于执行下次上传

    initUpload();
    
    //初始化上传
    function initUpload() {
     var chunk = 100 * 1024; //每片大小
     var input = document.getElementById("file"); //input file
     input.onchange = function (e) {
     var file = this.files[0];
     var query = {};
     var chunks = [];
     if (!!file) {
     var start = 0;
     //文件分片
     for (var i = 0; i < Math.ceil(file.size / chunk); i++) {
     var end = start + chunk;
     chunks[i] = file.slice(start , end);
     start = end;
     }
     
     // 采用post方法上传文件
     // url query上拼接以下参数,用于记录上传偏移
     // post body中存放本次要上传的二进制数据
     query = {
     fileSize: file.size,
     dataSize: chunk,
     nextOffset: 0
     }
    
     upload(chunks, query, successPerUpload);
     }
     }
    }
    
    // 执行上传
    function upload(chunks, query, cb) {
     var queryStr = Object.getOwnPropertyNames(query).map(key => {
     return key + "=" + query[key];
     }).join("&");
     var xhr = new XMLHttpRequest();
     xhr.open("POST", "http://xxxx/opload?" + queryStr);
     xhr.overrideMimeType("application/octet-stream");
     
     //获取post body中二进制数据
     var index = Math.floor(query.nextOffset / query.dataSize);
     getFileBinary(chunks[index], function (binary) {
     if (xhr.sendAsBinary) {
     xhr.sendAsBinary(binary);
     } else {
     xhr.send(binary);
     }
    
     });
    
     xhr.onreadystatechange = function (e) {
     if (xhr.readyState === 4) {
     if (xhr.status === 200) {
     var resp = JSON.parse(xhr.responseText);
     // 接口返回nextoffset
     // resp = {
     // isFinish:false,
     // offset:100*1024
     // }
     if (typeof cb === "function") {
     cb.call(this, resp, chunks, query)
     }
     }
     }
     }
    }
    
    // 每片上传成功后执行
    function successPerUpload(resp, chunks, query) {
     if (resp.isFinish === true) {
     alert("上传成功");
     } else {
     //未上传完毕
     query.offset = resp.offset;
     upload(chunks, query, successPerUpload);
     }
    }
    
    // 获取文件二进制数据
    function getFileBinary(file, cb) {
     var reader = new FileReader();
     reader.readAsArrayBuffer(file);
     reader.onload = function (e) {
     if (typeof cb === "function") {
     cb.call(this, this.result);
     }
     }
    }

    Blob URL

    blob协议的url使用时就像平时使用的url一样,可以作为图片请求地址,也可以作为文件请求地址。格式:

    blob:http://XXX

  • URL.createObjectURL(blob) 创建链接
  • URL.revokeObjectURL(url)
  • 下面是一个下载文件的示例,直接调用即可实现文件下载

    // file是要下载的文件(blob对象)
    downloadHandler: function (file, fileName) {
     let link = document.createElement('a')
     link.href = window.URL.createObjectURL(file)
     link.download = fileName
     link.click()
     window.URL.revokeObjectURL(link.href)
     if (navigator.userAgent.indexOf('Firefox') > -1) {
     const a = document.createElement('a')
     a.addEventListener('click', function (e) {
     a.download = fileName
     a.href = URL.createObjectURL(file)
     })
     let e = document.createEvent('MouseEvents')
     e.initEvent('click', false, false)
     a.dispatchEvent(e)
     }
    }

    在从后台获取的数据接口中把返回类型设置为blob

    var x = new XMLHttpRequest();
    x.responseType = 'blob'; // 返回一个blob对象 

    Blob URL和Data URL的区别

    Blob URL

    Data URL

  • Blob URL的长度一般比较短,但Data URL因为直接存储图片base64编码后的数据,往往很长,如上图所示,浏览器在显示Data URL时使用了省略号(…)。当显式大图片时,使用Blob URL能获取更好的可能性。
  • Blob URL可以方便的使用XMLHttpRequest获取源数据,比如设置XMLHttpRequest返回的数据类型为blob
  • Blob URL 只能在当前应用内部使用,把Blob URL复制到浏览器的地址栏中,是无法获取数据的。Data URL相比之下,就有很好的移植性,可以在任意浏览器中使用。
  • 总结

    Top