视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
利用Blob进行文件上传的完整步骤
2020-11-27 22:10:46 责编:小采
文档


构造函数

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

    下载本文
    显示全文
    专题