整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          「HTML5 進階」FileAPI 文件操作實戰,內

          「HTML5 進階」FileAPI 文件操作實戰,內附詳細案例,建議收藏

          ileAPI

          介紹

          HTML5 為我們提供了 File API 相關規范。主要涉及 File 接口 和 FileReader 對象 。

          本文整理了兼容性檢測、文件選擇、屬性讀取、文件讀取、進度監控、大文件分片上傳以及拖拽上傳等開發中常見的前端文件操作。

          準備工作

          首先,我們的 File 來自于 <input> 標簽中選中的文件列表。所以,準備如下的 HTML 代碼:

          <input type="file" id="files" multiple />
          <div id="list"></div>
          <div id="images"></div>
          <!-- File API相關操作寫在了script.js中 -->
          <script src="./script.js"></script>
          

          檢測兼容性

          File 對象是特殊類型的 Blob。在 script 入口處,應該檢測當前瀏覽器是否支持 File API

          if (!(window.File && window.FileReader && window.FileList && window.Blob)) {
           throw new Error("當前瀏覽器對FileAPI的支持不完善");
          }
          

          監聽文件選擇

          對于 typefile 類型的 <input> 標簽,在選擇文件的時候,會觸發 change 事件。用戶選中的文件信息也會傳入回調函數的第一個參數中。

          function handleFileSelect(event) {
           const { files }=event.target;
           if (!files.length) {
           console.log("沒有選擇文件");
           return;
           }
           console.log("選中的文件信息是:", files);
          }
          document
           .querySelector("#files")
           .addEventListener("change", handleFileSelect, false);
          

          文件屬性-File

          event.target.files 是一個 FileList 對象,它是一個由 File 對象組成的列表。

          每個 File 對象,保存著選中的對應文件的屬性。常用的用:

          • name:文件名
          • type:文件類型
          • size:文件大小

          下面,通過 type 屬性,過濾掉非圖片類型的文件,只展示圖片類型文件的信息:

          function handleFileSelect(event) {
           const { files }=event.target;
           if (!files.length) {
           console.log("沒有選擇文件");
           return;
           }
           const innerHTML=[];
           const reImage=/image.*/;
           for (let file of files) {
           if (!reImage.test(file.type)) {
           continue;
           }
           innerHTML.push(
           `
           <li>
           <strong>${file.name}</strong>
           (${file.type || "n/a"}) -
           ${file.size} bytes
           </li>
           `
           );
           }
           document.querySelector("#list").innerHTML=`<ul>${innerHTML.join("")}</ul>`;
          }
          

          FileReader

          讀取文件-FileReader

          還是以圖片讀取為例,讀取并且顯示所有的圖片類型文件。

          文件讀取需要使用 FileReader 對象,它常用 3 個回調方法:

          • onload: 文件讀取完成
          • onloadstart:文件上傳開始
          • onprogress : 文件上傳中觸發

          和Image類似,在讀取文件之前,需要先綁定事件處理。它讀取操作有:readAsArrayBufferreadAsDataURLreadAsBinaryStringreadAsText。傳入的參數就是 File 對象。

          那么這幾個方法有什么區別呢?不同的讀取方式,回調事件onload接受到的event.target.result不相同。比如,readAsDataURL 讀取的話,result 是一個圖片的 url

          下面就是讀取圖片文件,然后展示的一個例子:

          function handleFileSelect(event) {
           let { files }=event.target;
           if (!files.length) {
           return;
           }
           let vm=document.createDocumentFragment(),
           re=/image.*/,
           loaded=0, // 完成加載的圖片數量
           total=0; // 總共圖片數量
           // 統計image文件數量
           for (let file of files) {
           re.test(file.type) && total++;
           }
           // onloadstart回調
           const handleLoadStart=(ev, file)=>
           console.log(`>>> Start load ${file.name}`);
           // onload回調
           const handleOnload=(ev, file)=> {
           console.log(`<<< End load ${file.name}`);
           const img=document.createElement("img");
           img.height=250;
           img.width=250;
           img.src=ev.target.result;
           vm.appendChild(img);
           // 完成加載后,將其放入dom元素中
           if (++loaded===total) {
           document.querySelector("#images").appendChild(vm);
           }
           };
           for (let file of files) {
           if (!re.test(file.type)) {
           continue;
           }
           const reader=new FileReader();
           reader.onloadstart=ev=> handleLoadStart(ev, file);
           reader.onload=ev=> handleOnload(ev, file);
           // 讀取文件對象
           reader.readAsDataURL(file);
           }
          }
          document
           .querySelector("#files")
           .addEventListener("change", handleFileSelect, false);
          

          監控讀取進度

          在監控讀取進度的時候,主要是處理 FileReader 對象上的 onprogress 事件。

          下面的例子,請打開一個較大的文件來查看效果(否則一下就讀取完了):

          function handleFileSelect(event) {
           let { files }=event.target;
           if (!files.length) {
           return;
           }
           const handleLoadStart=(ev, file)=>
           console.log(`>>> Start load ${file.name}`);
           const handleProgress=(ev, file)=> {
           if (!ev.lengthComputable) {
           return;
           }
           // 計算進度,并且以百分比形式展示
           const percent=Math.round((ev.loaded / ev.total) * 100);
           console.log(`<<< Loding ${file.name}, progress is ${percent}%`);
           };
           for (let file of files) {
           const reader=new FileReader();
           reader.onloadstart=ev=> handleLoadStart(ev, file);
           reader.onprogress=ev=> handleProgress(ev, file);
           reader.readAsArrayBuffer(file);
           }
          }
          document
           .querySelector("#files")
           .addEventListener("change", handleFileSelect, false);
          

          slice

          大文件分片讀取

          在對于超大文件,一般采用分片上傳的思路解決。文章開頭有講到,FileBlob 的一個特例。而 Blob 上有一個 slice (https://developer.mozilla.org/zh-CN/docs/Web/API/Blob/slice)方法,通過它,前端就可以實現分片讀取大文件的操作。

          為了方便說明,請先準備好一個 txt 文件,文件內容就是:hello world

          示例代碼如下,代碼中只讀取前 5 個字節,由于每個英文字母占 1 個字節,所以打印結果應該是“hello”。

          function handleFileSelect(event) {
           let { files }=event.target;
           if (!files.length) {
           return;
           }
           // 為了方便說明,這里僅僅讀取第一個文件
           const file=files[0];
           // 讀取前5個字節的內容
           const blob=file.slice(0, 5);
           const reader=new FileReader();
           // 控制臺輸出結果應該是:hello
           reader.onload=ev=> console.log(ev.target.result);
           reader.readAsText(blob);
          }
          document
           .querySelector("#files")
           .addEventListener("change", handleFileSelect, false);
          

          拖拽上傳

          和前面所述的 File API 相關是完全一樣的。唯一需要特殊處理的是文件對象的獲取入口改變了。對于 <input> 標簽,監聽 onchange 事件,FileList 存放在 event.target.files 中;對于拖拽操作,FileList 存放在拖拽事件的回調函數參數里,通過 event.dataTransfer.files 訪問即可。

          需要修改一下 html 代碼:

          <!DOCTYPE html>
          <head>
           <meta charset="UTF-8">
           <style>
           #container {
           width: 300px;
           height: 300px;
           border: 3px dotted red;
           }
           </style>
          </head>
          <body>
           <div id="container"></div>
           <script src="./script.js"></script>
          </body>
          </html>
          

          腳本文件的代碼如下:

          function handleDropover(event) {
           event.stopPropagation();
           event.preventDefault();
          }
          function handleDrop(event) {
           event.stopPropagation();
           event.preventDefault();
           /***** 訪問拖拽文件 *****/
           const files=event.dataTransfer.files;
           console.log(files);
           /**********/
          }
          const target=document.querySelector("#container");
          target.addEventListener("dragover", handleDropover);
          target.addEventListener("drop", handleDrop);
          

          后端相關

          后端相關超出了本文的討論范圍,可以參考這篇文章(https://github.com/purplebamboo/blog/issues/17)。

          版權

          作者:心譚

          鏈接:https://juejin.im/post/5d35af63e51d454fa33b199e

          著作權歸作者所有。

          關注

          感謝閱讀,如果這篇文章對你又幫助,記得 點贊收藏轉發 喲。

          期待下次與你相遇 :)

          tml5文件分割上傳解決方案

          html5提供的文件API中可以輕松的對文件進行分割切片, 然后通過javascript異步處理向服務器傳輸數據, 突破對大文件上傳的限制, 同時異步處理在一定程度上也提高了文件上傳的效率。用戶體驗上也優于前述方案。

          index.html

          <!DOCTYPE html>
          <html>
          <head lang="en">
          <meta charset="UTF-8">
          <title>大文件上傳實例</title>
          <script type="text/javascript">
              const BYTES_PER_CHUNK=1024 * 1024; // 每個文件切片大小定為1MB .
              var slices;
              var totalSlices;
              //發送請求
              function sendRequest() {
                  var blob=document.getElementById('file').files[0];
                  var start=0;
                  var end;
                  var index=0;
                  // 計算文件切片總數
                  slices=Math.ceil(blob.size / BYTES_PER_CHUNK);
                  totalSlices=slices;
                  while(start < blob.size) {
                  end=start + BYTES_PER_CHUNK;
                  if(end > blob.size) {
                      end=blob.size;
                  }
                  uploadFile(blob, index, start, end);
                  start=end;
                  index++;
                  }
              }
              //上傳文件
              function uploadFile(blob, index, start, end) {
                  var xhr;
                  var fd;
                  var chunk;
                  xhr=new XMLHttpRequest();
                  xhr.onreadystatechange=function() {
                  if(xhr.readyState==4) {
                  if(xhr.responseText) {
                      alert(xhr.responseText);
                  }
                  slices--;
                  // 如果所有文件切片都成功發送,發送文件合并請求。
                  if(slices==0) {
                      mergeFile(blob);
                      alert('文件上傳完畢');
                  }
                  }
                  };
                  chunk=blob.slice(start,end);//切割文件
                  //構造form數據
                  fd=new FormData();
                  fd.append("file", chunk);
                  fd.append("name", blob.name);
                  fd.append("index", index);
                  xhr.open("POST", "upload.php", true);
                  //設置二進制文邊界件頭
                  xhr.setRequestHeader("X_Requested_With", location.href.split("/")[3].replace(/[^a-z]+/g, '$'));
                  xhr.send(fd);
              }
              function mergeFile(blob) {
                  var xhr;
                  var fd;
                  xhr=new XMLHttpRequest();
                  fd=new FormData();
                  fd.append("name", blob.name);
                  fd.append("index", totalSlices);
                  xhr.open("POST", "merge.php", true);
                  xhr.setRequestHeader("X_Requested_With", location.href.split("/")[3].replace(/[^a-z]+/g, '$'));
                  xhr.send(fd);
              }
          </script>
          </head>
          <body>
          <input type="file" id="file"/>
          <button onclick="sendRequest()">上傳</button>
          </body>
          </html>

          upload.php

          <?php
          //省略了文件接收判斷isset部分
          //當前目錄下建立一個uploads文件夾
          //接收文件名時進行轉碼,防止中文亂碼。
          $target="uploads/" .iconv("utf-8","gbk",$_POST["name"]) . '-' . $_POST['index'];
          move_uploaded_file($_FILES['file']['tmp_name'], $target);
          // Might execute too quickly.
          sleep(1);
          ?>

          merge.php

          <?php
          //文件合并
          $target="uploads/" .iconv("utf-8","gbk",$_POST["name"]);
          $dst=fopen($target, 'wb');
          for($i=0; $i < $_POST['index']; $i++) {
          $slice=$target . '-' . $i;
          $src=fopen($slice, 'rb');
          stream_copy_to_stream($src, $dst);
          fclose($src);
          unlink($slice);
          }
          fclose($dst);

          關鍵函數stream_copy_to_stream()

          int stream_copy_to_stream ( resource $source , resource $dest [, int $maxlength=-1 [, int $offset=0 ]] )

          <?php
          $src=fopen('http://www.example.com', 'r');
          $dest1=fopen('first1k.txt', 'w');
          $dest2=fopen('remainder.txt', 'w');
          echo stream_copy_to_stream($src, $dest1, 1024) . " bytes copied to first1k.txt\n";
          echo stream_copy_to_stream($src, $dest2) . " bytes copied to remainder.txt\n";
          ?>
          var blob=document.getElementById('file').files[0];
          console.dir(blob);

          相關的屬性如下:

          lastModified: 1511081596000

          lastModifiedDate: Sun Nov 19 2017 16:53:16 GMT+0800 (中國標準時間) {}

          name: "IMG_20171119_165316.jpg"

          size: 4383101

          type: "image/jpeg"

          slice: ? slice() 用于切割文件

          HTML5 中提供的文件API在前端中有著豐富的應用,上傳、下載、讀取內容等在日常的交互中很常見。而且在各個瀏覽器的兼容也比較好,包括移動端,除了 IE 只支持 IE10 以上的版本。想要更好地掌握好操作文件的功能,先要熟悉每個API。

          FileList 對象和 file 對象

          HTML 中的 input[type="file"] 標簽有個 multiple 屬性,允許用戶選擇多個文件,FileList對象則就是表示用戶選擇的文件列表。這個列表中的每一個文件,就是一個 file 對象。

          file 對象的屬性:

          • name : 文件名,不包含路徑。
          • type : 文件類型。圖片類型的文件都會以 image/ 開頭,可以由此來限制只允許上傳圖片。
          • size : 文件大小。可以根據文件大小來進行其他操作。
          • lastModified : 文件最后修改的時間。
          <input type="file" id="files" multiple>
          <script>
           var elem=document.getElementById('files');
           elem.onchange=function (event) {
           var files=event.target.files;
           for (var i=0; i < files.length; i++) {
           // 文件類型為 image 并且文件大小小于 200kb
           if(files[i].type.indexOf('image/') !==-1 && files[i].size < 204800){
           console.log(files[i].name);
           }
           }
           }
          </script>
          

          input 中有個 accept 屬性,可以用來規定能夠通過文件上傳進行提交的文件類型。

          accept="image/*" 可以用來限制只允許上傳圖像格式。但是在 Webkit 瀏覽器下卻出現了響應滯慢的問題,要等上好幾秒才彈出文件選擇框。

          解決方法就是將 * 通配符改為指定的 MIME 類型。

          <input type="file" accept="image/gif,image/jpeg,image/jpg,image/png">
          

          Blob 對象

          Blob 對象相當于一個容器,可以用于存放二進制數據。它有兩個屬性,size 屬性表示字節長度,type 屬性表示 MIME 類型。

          如何創建

          Blob 對象可以使用 Blob() 構造函數來創建。

          var blob=new Blob(['hello'], {type:"text/plain"});
          

          Blob 構造函數中的第一個參數是一個數組,可以存放 ArrayBuffer對象、ArrayBufferView 對象、Blob對象和字符串。

          Blob 對象可以通過 slice() 方法來返回一個新的 Blob 對象。

          var newblob=blob.slice(0,5, {type:"text/plain"});
          

          slice() 方法使用三個參數,均為可選。第一個參數代表要從Blob對象中的二進制數據的起始位置開始復制,第二個參數代表復制的結束位置,第三個參數為 Blob 對象的 MIME 類型。

          canvas.toBlob() 也可以創建 Blob 對象。toBlob() 使用三個參數,第一個為回調函數,第二個為圖片類型,默認為 image/png,第三個為圖片質量,值在0到1之間。

          var canvas=document.getElementById('canvas');
          canvas.toBlob(function(blob){ console.log(blob); }, "image/jpeg", 0.5);
          

          下載文件

          Blod 對象可以通過 window.URL 對象生成一個網絡地址,結合 a 標簽的 download 屬性來實現下載文件功能。

          比如把 canvas 下載為一個圖片文件。

          var canvas=document.getElementById('canvas');
          canvas.toBlob(function(blob){
           // 使用 createObjectURL 生成地址,格式為 blob:null/fd95b806-db11-4f98-b2ce-5eb16b38ba36
           var url=URL.createObjectURL(blob);
           var a=document.createElement('a');
           a.download='canvas';
           a.href=url;
           // 模擬a標簽點擊進行下載
           a.click();
           // 下載后告訴瀏覽器不再需要保持這個文件的引用了
           URL.revokeObjectURL(url);
          });
          

          也可以將字符串保存為一個文本文件,方法類似。

          FileReader 對象

          FileReader 對象主要用來把文件讀入內存,并且讀取文件中的數據。通過構造函數創建一個 FileReader 對象

          var reader=new FileReader();
          

          該對象有以下方法:

          • abort:中斷讀取操作。
          • readAsArrayBuffer:讀取文件內容到ArrayBuffer對象中。
          • readAsBinaryString:將文件讀取為二進制數據。
          • readAsDataURL:將文件讀取為data: URL格式的字符串。
          • readAsText:將文件讀取為文本。

          上傳圖片預覽

          在常見的應用就是在客戶端上傳圖片之后通過 readAsDataURL() 來顯示圖片。

          <input type="file" id="files" accept="image/jpeg,image/jpg,image/png">
          <img src="blank.gif" id="preview">
          <script>
           var elem=document.getElementById('files'),
           img=document.getElementById('preview');
           elem.onchange=function () {
           var files=elem.files,
           reader=new FileReader();
           if(files && files[0]){
           reader.onload=function (ev) {
           img.src=ev.target.result;
           }
           reader.readAsDataURL(files[0]);
           }
           }
          </script>
          

          但是在一些手機上豎著拍照上傳照片時會有bug,會發現照片倒了,包括三星和iPhone。。。解決方案這里不做講解,有興趣可以查看:移動端圖片上傳旋轉、壓縮的解決方案

          數據備份與恢復

          FileReader 對象的 readAsText() 可以讀取文件的文本,結合 Blob 對象下載文件的功能,那就可以實現將數據導出文件備份到本地,當數據要恢復時,通過 input 把備份文件上傳,使用 readAsText() 讀取文本,恢復數據。

          代碼跟上面功能類似,這里不重復,具體的應用可以參考:notepad

          Base64 編碼

          在 HTML5 中新增了 atob 和 btoa 方法來支持 Base64 編碼。它們的命名也很簡單,b to a 和 a to b,即代表著編碼和解碼。

          var a="https://lin-xin.github.io";
          var b=btoa(a);
          var c=atob(b);
          console.log(a); // https://lin-xin.github.io
          console.log(b); // aHR0cHM6Ly9saW4teGluLmdpdGh1Yi5pbw==console.log(c); // https://lin-xin.github.io
          

          btoa 方法對字符串 a 進行編碼,不會改變 a 的值,返回一個編碼后的值。atob 方法對編碼后的字符串進行解碼。

          但是參數中帶中文,已經超出了8位ASCII編碼的字符范圍,瀏覽器就會報錯。所以需要先對中文進行 encodeURIComponent 編碼處理。

          var a="哈嘍 世界";
          var b=btoa(encodeURIComponent(a));
          var c=decodeURIComponent(atob(b));
          console.log(b); // JUU1JTkzJTg4JUU1JTk2JUJEJTIwJUU0JUI4JTk2JUU3JTk1JThD
          console.log(c); // 哈嘍 世界
          

          https://zhuanlan.zhihu.com/p/27677175


          主站蜘蛛池模板: 精品无码AV一区二区三区不卡| 日韩在线一区高清在线| 免费观看一区二区三区| 91久久精品午夜一区二区| 日本中文字幕在线视频一区| 无码人妻精品一区二区三区9厂| 亚洲国产视频一区| 麻豆一区二区在我观看| 精品一区二区91| 亚洲AV福利天堂一区二区三| 成人国产精品一区二区网站公司 | 一区二区三区免费视频观看| 免费无码VA一区二区三区| 亚洲性日韩精品国产一区二区| 国产在线精品一区二区夜色| 日韩经典精品无码一区| 中文字幕日韩一区二区不卡| 日本在线视频一区二区三区| 中文字幕亚洲一区| 成人一区二区免费视频| 韩国福利一区二区三区高清视频| 琪琪see色原网一区二区| 免费萌白酱国产一区二区| 99久久精品午夜一区二区| 日产亚洲一区二区三区| 国产精品美女一区二区视频| 亚洲爆乳无码一区二区三区| 日本亚洲国产一区二区三区| 久久精品综合一区二区三区| 黄桃AV无码免费一区二区三区| 手机看片一区二区| 国产成人一区二区三区视频免费| 国产成人无码一区二区三区| 亚洲国产美女福利直播秀一区二区| 精品一区精品二区制服| 亚洲国产成人久久一区二区三区 | 国精产品一区一区三区有限在线| 91精品一区国产高清在线| 中文字幕乱码一区二区免费| 美女福利视频一区| 精品国产一区二区三区www|