JS___Blob处理二进制数据分片上传 23-11-17 19:41 2931 8767 blog.csdn.net <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Blobtitle> head> <body> <canvas id="canvas">canvas> <script> // Blob基本用法 // 创建 // 可以通过Blob的构造函数创建Blob对象: // Blob(blobParts[, options]) // 参数说明: // blobParts: 数组类型, 数组中的每一项连接起来构成Blob对象的数据,数组中的每项元素可以是ArrayBuffer(二进制数据缓冲区), ArrayBufferView,Blob,DOMString。或其他类似对象的混合体。 // options: 可选项,字典格式类型,可以指定如下两个属性: // type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。 // endings, 默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变。 // 举个栗子: var data1 = "a"; var data2 = "b"; var data3 = "This is a blob"; var data4 = { name: "abc" }; var blob1 = new Blob([data1]); var blob2 = new Blob([data1, data2]); var blob3 = new Blob([data3]); var blob4 = new Blob([JSON.stringify(data4)]); var blob5 = new Blob([data4]); var blob6 = new Blob([data3, data4]); console.log(blob1); // => Blob {size: 1, type: ""} console.log(blob2); // => Blob {size: 2, type: ""} console.log(blob3); // => Blob {size: 44, type: ""} console.log(blob4); // => Blob {size: 14, type: ""} console.log(blob5); // => Blob {size: 15, type: ""} console.log(blob6); // => Blob {size: 59, type: ""} // size代表Blob 对象中所包含数据的字节数。这里要注意,使用字符串和普通对象创建Blob时的不同,blob4使用通过 // JSON.stringify把data4对象转换成json字符串,blob5则直接使用data4创建,两个对象的size分别为14和15。blob4 // 的size等于14很容易理解,因为JSON.stringify(data4)的结果为:"{"name":"abc"}",正好14个字节(不包含最外层的 // 引号)。blob5的size等于15是如何计算而来的呢?实际上,当使用普通对象创建Blob对象时,相当于调用了普通对象的 // toString()方法得到字符串数据,然后再创建Blob对象。所以,blob5保存的数据是"[object Object]",是15个字节 // (不包含最外层的引号)。 // slice方法 Blob的slice方法操作的是二进制数据 使用方式类似于字符串的slice方法 // Blob对象有一个slice方法,返回一个新的Blob对象,包含了源Blob对象中制定范围内的数据。 // 参数说明: // start: 可选,代表 Blob 里的下标,表示第一个会被会被拷贝进新的 Blob 的字节的起始位置。如果传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。 // end: 可选,代表的是 Blob 的一个下标,这个下标-1的对应的字节将会是被拷贝进新的Blob 的最后一个字节。如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。 // contentType: 可选,给新的 Blob 赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。 // var data = "abcdef"; // var blob1 = new Blob([data]); // => Blob {size: 6, type: ""} // var blob2 = blob1.slice(0, 3); // => Blob {size: 3, type: ""} // console.log(blob1); // console.log(blob2); // 通过slice方法,从blob1中创建出一个新的blob对象,size等于3。 // Blob对象能够添加到表单中,作为上传数据使用: // const content = 'hey!'; // const blob = new Blob([content], { type: "text/xml" }); // formData.append("webmasterfile", blob); // Blob使用场景 // 分片上传 // 通过Blob.slice方法,可以将大文件分片,轮循向后台提交各文件片段,即可实现文件的分片上传。 // 分片上传逻辑如下: // 获取要上传文件的File对象,根据chunk(每片大小)对文件进行分片 // 通过post方法轮循上传每片文件,其中url中拼接querystring用于描述当前上传的文件信息;post body中存放本次要上传的二进制数据片段 // 接口每次返回offset,用于执行下次上传 // 下面是分片上传的简单实现: // 前面已经说过,File继承字Blob,因此我们可以调用slice方法对大文件进行分片上传。代码: function uploadFile(file) { var chunkSize = 1024 * 1024; // 每片1M大小 单位: 字节 byte var totalSize = file.size; // 二进制(文件)数据总大小 单位: 字节 byte var chunkQuantity = Math.ceil(totalSize/chunkSize); // 分片总次数 var offset = 0; //偏移量 var reader = new FileReader(); console.log(reader); // => {error: null, onabort: null, onerror: null, onload: null, onloadend: null, onloadstart: null, onprogress: null, readyState: 0, result: null} reader.onload = function(e) { var xhr = new XMLHttpRequest(); xhr.open("POST", url); xhr.overrideMimeType("application/octet-stream"); xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status ===200) { ++offset; if(offset === chunkQuantity) { console.log("上传完成"); } else if(offset === chunkQuantity - 1) { blob = file.slice(offset * chunkSize, totalSize); reader.readAsBinaryString(blob); // 递归思想读取指定的Blob中的内容, 一旦完成,result属性中将包含所读取文件的原始二进制数据。在读取后,触发onload事件, } else { blob = file.slice(offset * chunkSize, (offset + 1) * chunkSize); reader.readAsBinaryString(blob); // 递归思想读取指定的Blob中的内容, 一旦完成,result属性中将包含所读取文件的原始二进制数据。在读取后,触发onload事件, } }else { console.log("上传出错"); } } if(xhr.sendAsBinary) { // 过时的XMLHttpRequest方法sendAsBinary()是send()发送二进制数据的方法的一种变体。该send()方法现在支持二进制数据,应改为使用。 xhr.sendAsBinary(e.target.result); } else { xhr.send(e.target.result); } } // 第一次截取 截取0 到 1MB(1024 * 1024)二进制数据 单位: 字节 byte var blob = file.slice(0, chunkSize); // 开始读取指定的Blob中的内容,一旦完成,reader对象中result属性中将包含所读取文件的原始二进制数据。在读取后,触发reader.onload事件, reader.readAsBinaryString(blob); } // 这段代码还可以进一步丰富,比如显示当前上传进度,使用多个XMLHttpRequest对象并行上传对象(需要传递分片数据得位置参数给服务器端)等。 // Blob URL // Blob URL是blob协议得URL,它的格式如下: // blob:http://xxx // Blob URL可以通过URL.createObjectURL(blob)创建。在绝大部分场景下,我们可以像使用Http协议得URL一样使用Blob URL。常见得场景有: 作为文件得下载地址和作为图片资源地址。 // 文件下载地址 function createDownloadFile() { var content = "Blob Data"; var blob = new Blob([content]); // 数组中的元素可以是ArrayBuffer(二进制数据缓冲区) var link = document.getElementsByTagName("a")[0]; link.download = "file"; link.href = URL.createObjectURL(blob); } window.onload = createDownloadFile; // 点击下载按钮,浏览器将会下载一个名为file得文件,文件得内容是:Blob Data。通过Blob对象,我们在前端代码中就可以动态生成文件,提供 给浏览器下载。 // 图片资源地址 // 为图片文件创建一个Blob URL,赋值给标签: function handleFile(e) { var file = e.files[0]; var blob = URL.createObjectURL(file); var img = document.getElementsByTagName("img")[0]; console.log(blob); // => blob:http://127.0.0.1:5501/9af71368-b7e9-42e3-8c92-769ea63b9ec2 img.src = blob; img.onload = function(e) { URL.revokeObjectURL(this.src); //释放createObjectURL创建得对象 }; } // imput中选择得图片会在这里显示出来: // 同时,可以在Network标签栏,发现这个Blob URL得请求信息: // 这个请求信息和我们平常使用Http URL获取得图片几乎完全一样。 // window.URL.revokeObjectURL() // 在每次调用createObjectURL()方法时,都会创建一个新的URL对象,即使你已经用相同的对象作为参数创建过。当不再需要这些URL对象时,每个对象必须通过调用URL.revokeObjectURL()方法来释放。浏览器会在文档退出时自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。 // window.URL.revokeObjectURL(objectURL); // 我们还可以使用Data URL加载图片资源: function handleFile2(e) { var file = e.files[0]; var fileReader = new FileReader(); var img = document.getElementsByTagName("img")[0]; fileReader.onload = function(e) { img.src = e.target.result; }; fileReader.readAsDataURL(file); } // 那么Blob URL和Data URL有什么区别呢? // Blob URL得长度一般比较短,但Data URL因为直接存储图片base64编码后得数据,往往很长。当显示大图片时,使用Blob URL更优。 // Blob URL可以方便的使用XMLHttpRequest获取源数据,例如: var blobUrl = URL.createObjectURL( new Blob(["Test"], { type: "text/plain" }) ); var xhr = new XMLHttpRequest(); //如果是指xhr.responseType = 'blob',将返回一个Blob对象,而不是文本; //xhr.responseType = 'blob'; xhr.onload = function() { console.log(xhr.responseText); }; xhr.open("get", blobUrl); xhr.send(); // 对于Data URL, 并不是所有浏览器都支持通过XMLHttpRequest获取源数据的。 // Blob URL只能在当前应用内部使用,把Blob URL复制到浏览器的地址栏中,是无法获取数据的。Data URL相比之下,就有很好的移植性,你可以在任意浏览器使用。 // 除了可以用作图片资源的网络地址,Blob URL也可以用作其他资源的网络地址,例如html文件、json文件等,为了保证浏览器能正确的解析Blob URL返回的文件类型,需要在创建Blob对象时指定相应的type: // 创建HTML文件的Blob URL var data = ""; var blob = new Blob([data], { type: "text/html" }); // 'application/json' var blobUrl = URL.createObjectURL(blob); console.log(blobUrl); // 简书: //(1)blob对象:一直以来,JS都没有比较好的可以直接处理二进制的方法。而blob的存在,允许我们可以通过js直接操作二进制数据。 // “一个blob对象就是一个包含有只读原始数据的类文件对象。blob对象中的数据并不一定得是JavaScript中的原生形式。file接口基于blob,继承了blob的功能,并且扩展支持了用户计算机上的本地文件” // Blob对象可以看作是存放二进制数据的容器,此外还可以通过blob设置二进制数据的MIME类型。 //(2)创建blob // 方法一:通过构造函数 // var blob = new Blob(dataArr:Array,opt:{type:string}); // dataArr:数组,包含了要添加到blob对象中的数据,数据可以是任意多个ArrayBuffer(二进制数据缓冲区),ArrayBufferView,blob或者DOMString对象 // opt:对象,用于设置Blob对象的属性(如MIME类型) // type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。 // MIME:每一个 URL 都代表着一个资源对象,而当我们请求一个网页的时候, // 看似只请求了一个 URI(统一资源标识符),实际上这个网页可能包含多个 URI, // 例如图片资源的 URI 和视频资源的 URI 等。此时有些浏览器为了加快访问速度, // 可能会同时开多个线程去请求 URI。也就是说其实每一个 URI 都发送了一个请求报文。 // 而当我们的浏览器要显示或处理这些资源的时候,我们并不知道其响应的数据是什么类型的, // 为了区分这些资源类型,就需要用到 MIME 了。HTTP 会为每一个通过 web 传输的对象添加上 MIME 类型的数据格式标签。 // 浏览器在读取到对应的信息后,会调用相应的程序去处理它,任何得到我们想要的结果。 // 第一种:创建一个装填DOMString对象的blob对象 var s = "Hello world!!"; var blob = new Blob([s], { type: "text/xml" }); console.log(blob); // => Blob {size: 24, type: "text/xml"} // 第二种:创建一个装填ArrayBuffer对象的Blob对象 var abf = new ArrayBuffer(8); var blob = new Blob([abf], { type: "text/plain" }); console.log(blob); // => Blob {size: 8, type: "text/plain"} // 第三种:创建一个装填ArrayBufferView对象的Blob对象(ArrayBufferView可基于ArrayBuffer创建,返回值是一个类数组。如下,创建一个8字节的ArrayBuffer,在其上创建一个每个数组元素为2字节的“视图”) var abf = new ArrayBuffer(8); var abv = new Int16Array(abf); var blob = new Blob(abv, { type: "text/plain" }); console.log(blob); // => Blob {size: 4, type: "text/plain"} // 方法二:通过Blob.slice() 截取二进制数据 // 此方法返回一个新的Blob对象,包含了原blob对象中指定范围内的数据 // Blob.slice(start:number,end:number,contentType:string) // start:开始索引,默认为0 // end:截止结束索引(不包括end) // contentType:新blob的MIME类型,默认为空字符串"" var b = new Blob(["zzxxxccvvbb1111"], { type: "text/plain" }); var b2 = b.slice(0, 5, "text/plain"); console.log(b2); // => Blob {size: 5, type: "text/plain"} // 方法三:通过canvas.toBlob() var canvas = document.getElementById("canvas"); console.dir(canvas); // => canvas#canvas canvas.toBlob(function(blob) { console.log(blob); // => Blob {size: 1179, type: "image/png"} }); script> <h1>文件下载地址h1> <a>下载a> <br /> <hr /> <br /> <h1>图片资源地址h1> <input type="file" accept="image/*" onchange="handleFile(this)" /> <br /> <img style="width:200px;height:200px;" /> <hr /> <h1>加载图片资源h1> <input type="file" accept="image/*" onchange="handleFile2(this)" /> br> <img style="width:200px;height:200px;"> body> html> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 vue项目中导出excel表格 // 导出 handleExport() { this.$axios .get("/sys/log/error/export", { params: { token: this.getToken(document.cookie) }, responseType: "blob" }) .then(res => { console.log(res); // 导出excel表格 const link = document.createElement("a"); let blob = new Blob([res.data], { type: "application/vnd.ms-excel" }); link.style.display = "none"; link.href = URL.createObjectURL(blob); let num = ""; for (let i = 0; i < 10; i++) { num += Math.ceil(Math.random() * 10); } link.setAttribute("download", "用户_" + num + ".xls"); document.body.appendChild(link); link.click(); document.body.removeChild(link); }) .catch(error => { this.$Notice.error({ title: "错误", desc: "网络连接错误" }); // console.log(error); }); }, 123456789101112131415161718192021222324252627282930 案例: // 假设上传文件很大: Blob分片上传 var input = document.querySelector("#file"); var username = document.getElementsByClassName("username")[0]; var password = document.getElementsByClassName("password")[0]; input.onchange = function(event) { var file = event.target.files[0]; var chunkSize = 10000; // 每片10000字节大小 单位: byte var totalSize = file.size; // 二进制(文件)数据总大小 单位: 字节 byte var chunkQuantity = Math.ceil(totalSize / chunkSize); // 分片总次数 var offset = 0; var reader = new FileReader(); reader.onload = function(e) { var xhr = new XMLHttpRequest(); xhr.open("post", "upload.php", true); // 默认为true 表示异步 xhr.overrideMimeType("text/plain"); xhr.onreadystatechange = function() { console.log(xhr.readyState, xhr.status); if (xhr.readyState === 4 && xhr.status === 200) { offset++; if (offset === chunkQuantity) { console.log("上传完成"); } else if (offset === chunkQuantity - 1) { blob = file.slice(offset * chunkSize, totalSize); reader.readAsBinaryString(blob); } else { blob = file.slice(offset * chunkSize, (offset + 1) * chunkSize); reader.readAsBinaryString(blob); } } else { console.log("上传出错"); } }; xhr.send(e.target.result); }; // 第一次截取 0到10000 位置二进制数据 单位: 字节 byte var blob = file.slice(0, chunkSize); // 开始读取指定的Blob中的内容,一旦完成,reader对象中result属性中将包含所读取文件的原始二进制数据。在读取后,触发reader.onload事件, reader.readAsBinaryString(blob); }; 123456789101112131415161718192021222324252627282930313233343536373839 upload.php: print_r(json_encode($_FILES)); var_dump(json_encode($_POST)); 1234 效果如下: id="article_content" class="article_content clearfix"> id="content_views" class="htmledit_views"> 我是从大二开始接触计算机的,当前已经大三,在计算机这条道路上已经走了一年多的时间。回望这一年多的时间,我认为我自己是无愧于 "努力" 二字的。 最初接触计算机也是偶然,大二开学的时候开始上学校开设的C语言课程,上课时尽管认真听讲,还是感觉一头雾水,不知所措,于是下课后我就会回到宿舍自己去网上找课程自学,但是还是学的磕磕绊绊。就这么坚持了几周后,突然在学到某一个节点时就像是打通了任督二脉一般,逐渐开始理解前面所学的知识,所有的知识点都串通了起来,在这之后的学习,没有了最开始学习的痛苦和不理解,反而越发开始期待后面的学习内容。也是从这时候开始,我发现我并不喜欢自己当前的专业,在这之前,我大一时候的学习,都是秉持着 "有课就上,下课就浪" 的学习精神,每天上完课就回到宿舍打游戏,学习的时间仅有课程上的那点时间。于是我在接触了C语言后便毅然决然地选择走计算机这条道路,后面也参加了转专业的考试,但是由于那时候才刚学完C语言,数据结构和算法的基础并不多,因此转专业的考试并没有通过。但同时,正因为这次考试激发了我的好胜心,因为考试不通过的不甘,让我比刚开始学C语言更加努力地学习后面的内容,这个时候我也开始维护我的《C语言》专栏博客,将我学习的知识复习并梳理出来,写成博客来供打家学习,同时也方便自己复习。 后面有了一定的基础之后,我开始尝试着刷题,首先接触的就是LeetCode,在刷第一道题,看到题目的时候我就蒙圈了,完全不知道要做什么,并且给出来的函数也是看不懂,完全不知道从哪里下手,都说刚开始刷题的时候十分痛苦,但是我觉得并不是刚开始刷题痛苦,而是有了一定的题量后,继续提升难度刷后面的题才是痛苦,刚开始刷题时完全就是懵的状态,何谈痛不痛苦。在有了一定的刷体量后,我开设了《每日刷题》专栏,到目前为止已经有180篇了,每篇博客基本都是三道题,从最开始全都是简单题,到后面基本都是中等困难题,里面每一题都十分详细的描述、描绘了解题思路、解题算法以及解题过程。 再后面,开始接触并学习C++,我以为有了C语言的基础,学习C++就是信手拈来的事情,直到我学到后面。C++虽说是从C发展出来的,但是难度却是一个质的飞跃,学习C++的过程中我甚至开始怀疑自己是否有智力缺陷,学起来痛苦不说,自己上手写更是不知所措。好在我并没有放弃,还是用我学习C语言的老方法来学习C++——课程上认真学,课后梳理所学知识整理为博客来复习,目前《C++》专栏也有了27篇博客,里面细致讲解了学习的过程、遇到的麻烦和坑。就这样慢慢地也学习完了C++的内容。 到现在,我也还正在学习Linux操作系统以及MySQL等知识。 回望这一年多的学习,我觉得我无愧于我认为的 "努力",课上认真学习,课下认真复习,脚踏实地地敲好每一行代码,认真地思考每一道题目,耐心地解决每一个难题,虽然不知道以后究竟会怎样,能否找到一个满意的工作,能否过上期盼的生活,但至少在这个过程中我无愧于自己,我并没有觉得我虚度了这些光阴,这就够了。 每个人都只活一次,无论怎么走、走什么样的路,都是人生的一部分,所有的路没有好坏之分,只要自己认为是想要走的路,那就坚持不懈地走下去,无论结果是什么,这条路都是专属于你的。 >>
<html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Blobtitle> head> <body> <canvas id="canvas">canvas> <script> // Blob基本用法 // 创建 // 可以通过Blob的构造函数创建Blob对象: // Blob(blobParts[, options]) // 参数说明: // blobParts: 数组类型, 数组中的每一项连接起来构成Blob对象的数据,数组中的每项元素可以是ArrayBuffer(二进制数据缓冲区), ArrayBufferView,Blob,DOMString。或其他类似对象的混合体。 // options: 可选项,字典格式类型,可以指定如下两个属性: // type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。 // endings, 默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: "native",表示行结束符会被更改为适合宿主操作系统文件系统的换行符; "transparent",表示会保持blob中保存的结束符不变。 // 举个栗子: var data1 = "a"; var data2 = "b"; var data3 = "This is a blob"; var data4 = { name: "abc" }; var blob1 = new Blob([data1]); var blob2 = new Blob([data1, data2]); var blob3 = new Blob([data3]); var blob4 = new Blob([JSON.stringify(data4)]); var blob5 = new Blob([data4]); var blob6 = new Blob([data3, data4]); console.log(blob1); // => Blob {size: 1, type: ""} console.log(blob2); // => Blob {size: 2, type: ""} console.log(blob3); // => Blob {size: 44, type: ""} console.log(blob4); // => Blob {size: 14, type: ""} console.log(blob5); // => Blob {size: 15, type: ""} console.log(blob6); // => Blob {size: 59, type: ""} // size代表Blob 对象中所包含数据的字节数。这里要注意,使用字符串和普通对象创建Blob时的不同,blob4使用通过 // JSON.stringify把data4对象转换成json字符串,blob5则直接使用data4创建,两个对象的size分别为14和15。blob4 // 的size等于14很容易理解,因为JSON.stringify(data4)的结果为:"{"name":"abc"}",正好14个字节(不包含最外层的 // 引号)。blob5的size等于15是如何计算而来的呢?实际上,当使用普通对象创建Blob对象时,相当于调用了普通对象的 // toString()方法得到字符串数据,然后再创建Blob对象。所以,blob5保存的数据是"[object Object]",是15个字节 // (不包含最外层的引号)。 // slice方法 Blob的slice方法操作的是二进制数据 使用方式类似于字符串的slice方法 // Blob对象有一个slice方法,返回一个新的Blob对象,包含了源Blob对象中制定范围内的数据。 // 参数说明: // start: 可选,代表 Blob 里的下标,表示第一个会被会被拷贝进新的 Blob 的字节的起始位置。如果传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。 // end: 可选,代表的是 Blob 的一个下标,这个下标-1的对应的字节将会是被拷贝进新的Blob 的最后一个字节。如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。 // contentType: 可选,给新的 Blob 赋予一个新的文档类型。这将会把它的 type 属性设为被传入的值。它的默认值是一个空的字符串。 // var data = "abcdef"; // var blob1 = new Blob([data]); // => Blob {size: 6, type: ""} // var blob2 = blob1.slice(0, 3); // => Blob {size: 3, type: ""} // console.log(blob1); // console.log(blob2); // 通过slice方法,从blob1中创建出一个新的blob对象,size等于3。 // Blob对象能够添加到表单中,作为上传数据使用: // const content = 'hey!'; // const blob = new Blob([content], { type: "text/xml" }); // formData.append("webmasterfile", blob); // Blob使用场景 // 分片上传 // 通过Blob.slice方法,可以将大文件分片,轮循向后台提交各文件片段,即可实现文件的分片上传。 // 分片上传逻辑如下: // 获取要上传文件的File对象,根据chunk(每片大小)对文件进行分片 // 通过post方法轮循上传每片文件,其中url中拼接querystring用于描述当前上传的文件信息;post body中存放本次要上传的二进制数据片段 // 接口每次返回offset,用于执行下次上传 // 下面是分片上传的简单实现: // 前面已经说过,File继承字Blob,因此我们可以调用slice方法对大文件进行分片上传。代码: function uploadFile(file) { var chunkSize = 1024 * 1024; // 每片1M大小 单位: 字节 byte var totalSize = file.size; // 二进制(文件)数据总大小 单位: 字节 byte var chunkQuantity = Math.ceil(totalSize/chunkSize); // 分片总次数 var offset = 0; //偏移量 var reader = new FileReader(); console.log(reader); // => {error: null, onabort: null, onerror: null, onload: null, onloadend: null, onloadstart: null, onprogress: null, readyState: 0, result: null} reader.onload = function(e) { var xhr = new XMLHttpRequest(); xhr.open("POST", url); xhr.overrideMimeType("application/octet-stream"); xhr.onreadystatechange = function() { if(xhr.readyState === 4 && xhr.status ===200) { ++offset; if(offset === chunkQuantity) { console.log("上传完成"); } else if(offset === chunkQuantity - 1) { blob = file.slice(offset * chunkSize, totalSize); reader.readAsBinaryString(blob); // 递归思想读取指定的Blob中的内容, 一旦完成,result属性中将包含所读取文件的原始二进制数据。在读取后,触发onload事件, } else { blob = file.slice(offset * chunkSize, (offset + 1) * chunkSize); reader.readAsBinaryString(blob); // 递归思想读取指定的Blob中的内容, 一旦完成,result属性中将包含所读取文件的原始二进制数据。在读取后,触发onload事件, } }else { console.log("上传出错"); } } if(xhr.sendAsBinary) { // 过时的XMLHttpRequest方法sendAsBinary()是send()发送二进制数据的方法的一种变体。该send()方法现在支持二进制数据,应改为使用。 xhr.sendAsBinary(e.target.result); } else { xhr.send(e.target.result); } } // 第一次截取 截取0 到 1MB(1024 * 1024)二进制数据 单位: 字节 byte var blob = file.slice(0, chunkSize); // 开始读取指定的Blob中的内容,一旦完成,reader对象中result属性中将包含所读取文件的原始二进制数据。在读取后,触发reader.onload事件, reader.readAsBinaryString(blob); } // 这段代码还可以进一步丰富,比如显示当前上传进度,使用多个XMLHttpRequest对象并行上传对象(需要传递分片数据得位置参数给服务器端)等。 // Blob URL // Blob URL是blob协议得URL,它的格式如下: // blob:http://xxx // Blob URL可以通过URL.createObjectURL(blob)创建。在绝大部分场景下,我们可以像使用Http协议得URL一样使用Blob URL。常见得场景有: 作为文件得下载地址和作为图片资源地址。 // 文件下载地址 function createDownloadFile() { var content = "Blob Data"; var blob = new Blob([content]); // 数组中的元素可以是ArrayBuffer(二进制数据缓冲区) var link = document.getElementsByTagName("a")[0]; link.download = "file"; link.href = URL.createObjectURL(blob); } window.onload = createDownloadFile; // 点击下载按钮,浏览器将会下载一个名为file得文件,文件得内容是:Blob Data。通过Blob对象,我们在前端代码中就可以动态生成文件,提供 给浏览器下载。 // 图片资源地址 // 为图片文件创建一个Blob URL,赋值给标签: function handleFile(e) { var file = e.files[0]; var blob = URL.createObjectURL(file); var img = document.getElementsByTagName("img")[0]; console.log(blob); // => blob:http://127.0.0.1:5501/9af71368-b7e9-42e3-8c92-769ea63b9ec2 img.src = blob; img.onload = function(e) { URL.revokeObjectURL(this.src); //释放createObjectURL创建得对象 }; } // imput中选择得图片会在这里显示出来: // 同时,可以在Network标签栏,发现这个Blob URL得请求信息: // 这个请求信息和我们平常使用Http URL获取得图片几乎完全一样。 // window.URL.revokeObjectURL() // 在每次调用createObjectURL()方法时,都会创建一个新的URL对象,即使你已经用相同的对象作为参数创建过。当不再需要这些URL对象时,每个对象必须通过调用URL.revokeObjectURL()方法来释放。浏览器会在文档退出时自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。 // window.URL.revokeObjectURL(objectURL); // 我们还可以使用Data URL加载图片资源: function handleFile2(e) { var file = e.files[0]; var fileReader = new FileReader(); var img = document.getElementsByTagName("img")[0]; fileReader.onload = function(e) { img.src = e.target.result; }; fileReader.readAsDataURL(file); } // 那么Blob URL和Data URL有什么区别呢? // Blob URL得长度一般比较短,但Data URL因为直接存储图片base64编码后得数据,往往很长。当显示大图片时,使用Blob URL更优。 // Blob URL可以方便的使用XMLHttpRequest获取源数据,例如: var blobUrl = URL.createObjectURL( new Blob(["Test"], { type: "text/plain" }) ); var xhr = new XMLHttpRequest(); //如果是指xhr.responseType = 'blob',将返回一个Blob对象,而不是文本; //xhr.responseType = 'blob'; xhr.onload = function() { console.log(xhr.responseText); }; xhr.open("get", blobUrl); xhr.send(); // 对于Data URL, 并不是所有浏览器都支持通过XMLHttpRequest获取源数据的。 // Blob URL只能在当前应用内部使用,把Blob URL复制到浏览器的地址栏中,是无法获取数据的。Data URL相比之下,就有很好的移植性,你可以在任意浏览器使用。 // 除了可以用作图片资源的网络地址,Blob URL也可以用作其他资源的网络地址,例如html文件、json文件等,为了保证浏览器能正确的解析Blob URL返回的文件类型,需要在创建Blob对象时指定相应的type: // 创建HTML文件的Blob URL var data = ""; var blob = new Blob([data], { type: "text/html" }); // 'application/json' var blobUrl = URL.createObjectURL(blob); console.log(blobUrl); // 简书: //(1)blob对象:一直以来,JS都没有比较好的可以直接处理二进制的方法。而blob的存在,允许我们可以通过js直接操作二进制数据。 // “一个blob对象就是一个包含有只读原始数据的类文件对象。blob对象中的数据并不一定得是JavaScript中的原生形式。file接口基于blob,继承了blob的功能,并且扩展支持了用户计算机上的本地文件” // Blob对象可以看作是存放二进制数据的容器,此外还可以通过blob设置二进制数据的MIME类型。 //(2)创建blob // 方法一:通过构造函数 // var blob = new Blob(dataArr:Array,opt:{type:string}); // dataArr:数组,包含了要添加到blob对象中的数据,数据可以是任意多个ArrayBuffer(二进制数据缓冲区),ArrayBufferView,blob或者DOMString对象 // opt:对象,用于设置Blob对象的属性(如MIME类型) // type,默认值为"",它代表了将会被放入到blob中的数组内容的MIME类型。 // MIME:每一个 URL 都代表着一个资源对象,而当我们请求一个网页的时候, // 看似只请求了一个 URI(统一资源标识符),实际上这个网页可能包含多个 URI, // 例如图片资源的 URI 和视频资源的 URI 等。此时有些浏览器为了加快访问速度, // 可能会同时开多个线程去请求 URI。也就是说其实每一个 URI 都发送了一个请求报文。 // 而当我们的浏览器要显示或处理这些资源的时候,我们并不知道其响应的数据是什么类型的, // 为了区分这些资源类型,就需要用到 MIME 了。HTTP 会为每一个通过 web 传输的对象添加上 MIME 类型的数据格式标签。 // 浏览器在读取到对应的信息后,会调用相应的程序去处理它,任何得到我们想要的结果。 // 第一种:创建一个装填DOMString对象的blob对象 var s = "Hello world!!"; var blob = new Blob([s], { type: "text/xml" }); console.log(blob); // => Blob {size: 24, type: "text/xml"} // 第二种:创建一个装填ArrayBuffer对象的Blob对象 var abf = new ArrayBuffer(8); var blob = new Blob([abf], { type: "text/plain" }); console.log(blob); // => Blob {size: 8, type: "text/plain"} // 第三种:创建一个装填ArrayBufferView对象的Blob对象(ArrayBufferView可基于ArrayBuffer创建,返回值是一个类数组。如下,创建一个8字节的ArrayBuffer,在其上创建一个每个数组元素为2字节的“视图”) var abf = new ArrayBuffer(8); var abv = new Int16Array(abf); var blob = new Blob(abv, { type: "text/plain" }); console.log(blob); // => Blob {size: 4, type: "text/plain"} // 方法二:通过Blob.slice() 截取二进制数据 // 此方法返回一个新的Blob对象,包含了原blob对象中指定范围内的数据 // Blob.slice(start:number,end:number,contentType:string) // start:开始索引,默认为0 // end:截止结束索引(不包括end) // contentType:新blob的MIME类型,默认为空字符串"" var b = new Blob(["zzxxxccvvbb1111"], { type: "text/plain" }); var b2 = b.slice(0, 5, "text/plain"); console.log(b2); // => Blob {size: 5, type: "text/plain"} // 方法三:通过canvas.toBlob() var canvas = document.getElementById("canvas"); console.dir(canvas); // => canvas#canvas canvas.toBlob(function(blob) { console.log(blob); // => Blob {size: 1179, type: "image/png"} }); script> <h1>文件下载地址h1> <a>下载a> <br /> <hr /> <br /> <h1>图片资源地址h1> <input type="file" accept="image/*" onchange="handleFile(this)" /> <br /> <img style="width:200px;height:200px;" /> <hr /> <h1>加载图片资源h1> <input type="file" accept="image/*" onchange="handleFile2(this)" /> br> <img style="width:200px;height:200px;"> body> html> 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 vue项目中导出excel表格 // 导出 handleExport() { this.$axios .get("/sys/log/error/export", { params: { token: this.getToken(document.cookie) }, responseType: "blob" }) .then(res => { console.log(res); // 导出excel表格 const link = document.createElement("a"); let blob = new Blob([res.data], { type: "application/vnd.ms-excel" }); link.style.display = "none"; link.href = URL.createObjectURL(blob); let num = ""; for (let i = 0; i < 10; i++) { num += Math.ceil(Math.random() * 10); } link.setAttribute("download", "用户_" + num + ".xls"); document.body.appendChild(link); link.click(); document.body.removeChild(link); }) .catch(error => { this.$Notice.error({ title: "错误", desc: "网络连接错误" }); // console.log(error); }); }, 123456789101112131415161718192021222324252627282930 案例: // 假设上传文件很大: Blob分片上传 var input = document.querySelector("#file"); var username = document.getElementsByClassName("username")[0]; var password = document.getElementsByClassName("password")[0]; input.onchange = function(event) { var file = event.target.files[0]; var chunkSize = 10000; // 每片10000字节大小 单位: byte var totalSize = file.size; // 二进制(文件)数据总大小 单位: 字节 byte var chunkQuantity = Math.ceil(totalSize / chunkSize); // 分片总次数 var offset = 0; var reader = new FileReader(); reader.onload = function(e) { var xhr = new XMLHttpRequest(); xhr.open("post", "upload.php", true); // 默认为true 表示异步 xhr.overrideMimeType("text/plain"); xhr.onreadystatechange = function() { console.log(xhr.readyState, xhr.status); if (xhr.readyState === 4 && xhr.status === 200) { offset++; if (offset === chunkQuantity) { console.log("上传完成"); } else if (offset === chunkQuantity - 1) { blob = file.slice(offset * chunkSize, totalSize); reader.readAsBinaryString(blob); } else { blob = file.slice(offset * chunkSize, (offset + 1) * chunkSize); reader.readAsBinaryString(blob); } } else { console.log("上传出错"); } }; xhr.send(e.target.result); }; // 第一次截取 0到10000 位置二进制数据 单位: 字节 byte var blob = file.slice(0, chunkSize); // 开始读取指定的Blob中的内容,一旦完成,reader对象中result属性中将包含所读取文件的原始二进制数据。在读取后,触发reader.onload事件, reader.readAsBinaryString(blob); }; 123456789101112131415161718192021222324252627282930313233343536373839 upload.php: print_r(json_encode($_FILES)); var_dump(json_encode($_POST)); 1234 效果如下: id="article_content" class="article_content clearfix"> id="content_views" class="htmledit_views"> 我是从大二开始接触计算机的,当前已经大三,在计算机这条道路上已经走了一年多的时间。回望这一年多的时间,我认为我自己是无愧于 "努力" 二字的。 最初接触计算机也是偶然,大二开学的时候开始上学校开设的C语言课程,上课时尽管认真听讲,还是感觉一头雾水,不知所措,于是下课后我就会回到宿舍自己去网上找课程自学,但是还是学的磕磕绊绊。就这么坚持了几周后,突然在学到某一个节点时就像是打通了任督二脉一般,逐渐开始理解前面所学的知识,所有的知识点都串通了起来,在这之后的学习,没有了最开始学习的痛苦和不理解,反而越发开始期待后面的学习内容。也是从这时候开始,我发现我并不喜欢自己当前的专业,在这之前,我大一时候的学习,都是秉持着 "有课就上,下课就浪" 的学习精神,每天上完课就回到宿舍打游戏,学习的时间仅有课程上的那点时间。于是我在接触了C语言后便毅然决然地选择走计算机这条道路,后面也参加了转专业的考试,但是由于那时候才刚学完C语言,数据结构和算法的基础并不多,因此转专业的考试并没有通过。但同时,正因为这次考试激发了我的好胜心,因为考试不通过的不甘,让我比刚开始学C语言更加努力地学习后面的内容,这个时候我也开始维护我的《C语言》专栏博客,将我学习的知识复习并梳理出来,写成博客来供打家学习,同时也方便自己复习。 后面有了一定的基础之后,我开始尝试着刷题,首先接触的就是LeetCode,在刷第一道题,看到题目的时候我就蒙圈了,完全不知道要做什么,并且给出来的函数也是看不懂,完全不知道从哪里下手,都说刚开始刷题的时候十分痛苦,但是我觉得并不是刚开始刷题痛苦,而是有了一定的题量后,继续提升难度刷后面的题才是痛苦,刚开始刷题时完全就是懵的状态,何谈痛不痛苦。在有了一定的刷体量后,我开设了《每日刷题》专栏,到目前为止已经有180篇了,每篇博客基本都是三道题,从最开始全都是简单题,到后面基本都是中等困难题,里面每一题都十分详细的描述、描绘了解题思路、解题算法以及解题过程。 再后面,开始接触并学习C++,我以为有了C语言的基础,学习C++就是信手拈来的事情,直到我学到后面。C++虽说是从C发展出来的,但是难度却是一个质的飞跃,学习C++的过程中我甚至开始怀疑自己是否有智力缺陷,学起来痛苦不说,自己上手写更是不知所措。好在我并没有放弃,还是用我学习C语言的老方法来学习C++——课程上认真学,课后梳理所学知识整理为博客来复习,目前《C++》专栏也有了27篇博客,里面细致讲解了学习的过程、遇到的麻烦和坑。就这样慢慢地也学习完了C++的内容。 到现在,我也还正在学习Linux操作系统以及MySQL等知识。 回望这一年多的学习,我觉得我无愧于我认为的 "努力",课上认真学习,课下认真复习,脚踏实地地敲好每一行代码,认真地思考每一道题目,耐心地解决每一个难题,虽然不知道以后究竟会怎样,能否找到一个满意的工作,能否过上期盼的生活,但至少在这个过程中我无愧于自己,我并没有觉得我虚度了这些光阴,这就够了。 每个人都只活一次,无论怎么走、走什么样的路,都是人生的一部分,所有的路没有好坏之分,只要自己认为是想要走的路,那就坚持不懈地走下去,无论结果是什么,这条路都是专属于你的。 >>
id="article_content" class="article_content clearfix"> id="content_views" class="htmledit_views"> 我是从大二开始接触计算机的,当前已经大三,在计算机这条道路上已经走了一年多的时间。回望这一年多的时间,我认为我自己是无愧于 "努力" 二字的。 最初接触计算机也是偶然,大二开学的时候开始上学校开设的C语言课程,上课时尽管认真听讲,还是感觉一头雾水,不知所措,于是下课后我就会回到宿舍自己去网上找课程自学,但是还是学的磕磕绊绊。就这么坚持了几周后,突然在学到某一个节点时就像是打通了任督二脉一般,逐渐开始理解前面所学的知识,所有的知识点都串通了起来,在这之后的学习,没有了最开始学习的痛苦和不理解,反而越发开始期待后面的学习内容。也是从这时候开始,我发现我并不喜欢自己当前的专业,在这之前,我大一时候的学习,都是秉持着 "有课就上,下课就浪" 的学习精神,每天上完课就回到宿舍打游戏,学习的时间仅有课程上的那点时间。于是我在接触了C语言后便毅然决然地选择走计算机这条道路,后面也参加了转专业的考试,但是由于那时候才刚学完C语言,数据结构和算法的基础并不多,因此转专业的考试并没有通过。但同时,正因为这次考试激发了我的好胜心,因为考试不通过的不甘,让我比刚开始学C语言更加努力地学习后面的内容,这个时候我也开始维护我的《C语言》专栏博客,将我学习的知识复习并梳理出来,写成博客来供打家学习,同时也方便自己复习。 后面有了一定的基础之后,我开始尝试着刷题,首先接触的就是LeetCode,在刷第一道题,看到题目的时候我就蒙圈了,完全不知道要做什么,并且给出来的函数也是看不懂,完全不知道从哪里下手,都说刚开始刷题的时候十分痛苦,但是我觉得并不是刚开始刷题痛苦,而是有了一定的题量后,继续提升难度刷后面的题才是痛苦,刚开始刷题时完全就是懵的状态,何谈痛不痛苦。在有了一定的刷体量后,我开设了《每日刷题》专栏,到目前为止已经有180篇了,每篇博客基本都是三道题,从最开始全都是简单题,到后面基本都是中等困难题,里面每一题都十分详细的描述、描绘了解题思路、解题算法以及解题过程。 再后面,开始接触并学习C++,我以为有了C语言的基础,学习C++就是信手拈来的事情,直到我学到后面。C++虽说是从C发展出来的,但是难度却是一个质的飞跃,学习C++的过程中我甚至开始怀疑自己是否有智力缺陷,学起来痛苦不说,自己上手写更是不知所措。好在我并没有放弃,还是用我学习C语言的老方法来学习C++——课程上认真学,课后梳理所学知识整理为博客来复习,目前《C++》专栏也有了27篇博客,里面细致讲解了学习的过程、遇到的麻烦和坑。就这样慢慢地也学习完了C++的内容。 到现在,我也还正在学习Linux操作系统以及MySQL等知识。 回望这一年多的学习,我觉得我无愧于我认为的 "努力",课上认真学习,课下认真复习,脚踏实地地敲好每一行代码,认真地思考每一道题目,耐心地解决每一个难题,虽然不知道以后究竟会怎样,能否找到一个满意的工作,能否过上期盼的生活,但至少在这个过程中我无愧于自己,我并没有觉得我虚度了这些光阴,这就够了。 每个人都只活一次,无论怎么走、走什么样的路,都是人生的一部分,所有的路没有好坏之分,只要自己认为是想要走的路,那就坚持不懈地走下去,无论结果是什么,这条路都是专属于你的。