前端下載文件以及上傳圖片預覽,順便了解arrayBuffer和blob

TL;DR

  • 前端下載的關鍵靠<a href="demo.jpg" download>下載圖片</a>
  • 後端傳blob,前端處理blob,而後下載文件
  • 二進制數組arrayBuffer、typedArray、dataView相關內容
  • blob、file相關內容
  • 上傳圖片預覽的兩種方式:blobURL和dataURL

下載的關鍵----a的download屬性

想要實現點擊加載,就能下載文件的功能。 會經常用到a標籤的down屬性。javascript

好比查看某圖片<a href="demo.jpg">查看圖片</a>,這個代碼常見,可是點擊會打開一個新頁面,因此關鍵點就是<a href="demo.jpg" download>下載圖片</a>,這裏點擊後就會彈出對話框保存在哪,以及編輯默認文件名。若是須要另外指定文件名的話,能夠用download="demo_1.jpg"html

須要注意,下載文件的網頁打開須要http開頭不能file開頭,也就是本地測試的時候,須要啓動本地服務。前端

前端實現下載任意服務器的任意的文件

常常可能須要下載pdf excel jpg之類的。其實能夠同種操做。 若是不瞭解二進制數組和blob,須要看後面。java

// 前端,建個index.html文件放進這些,啓動本地服務(node server.js)
// fetch的方式
      fetch("/down")
        .then(res => res.blob())
        .then(blob => {
          down(blob, "demo.pdf");
        });
      // 普通的方式
      request("/down").then(blob => {
        var blob = new Blob([blob]);
        down(blob, "demo1.pdf");
      });
      function down(blob, filename) {
        var blobURL = window.URL.createObjectURL(blob);
        var a = document.createElement("a");
        a.href = blobURL;
        // 表示下載的
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        a.remove();
        window.URL.revokeObjectURL(blobURL);
      }
      function request(url, method = "GET") {
        return new Promise((resolve, reject) => {
          var xhr = new XMLHttpRequest();
          xhr.open(method, url);
          // !!! 敲黑板 若是後端傳過來的時候blob或者buffer之類的 這句必須加
          xhr.responseType = "arraybuffer";
          xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
              resolve(xhr.response);
            }
          };
          xhr.send();
        });
      }

// -------- 後端 -------
// 引入本身的express路徑 不用express固然也行 這就是server.js
const express = require('/usr/local/lib/node_modules/express')
let app = express()
app.listen(7777)
app.get('/',(req,res)=>{
    res.sendFile(path.join(__dirname,'index.html'))
})

app.get('/down',(req,res)=>{
    // 注意必須有一個demo.pdf的文件
    res.download('demo.pdf')

// 瀏覽器打開 localhost:7777 沒出錯的話就能下載文件了 
複製代碼

其實說到這裏,再來一個小玩法,好比後端傳過來一串buffer怎麼玩。node

// 好比後端加一個這個
app.get('/buffer',(req,res)=>{
    res.send(Buffer.from('花花天下第一美'))
})

// 猜前端怎麼玩
request("/buffer").then(response => {
    // 提取uint8Array
    let uint8 = new Uint8Array(response);
    // 解決亂碼
    let resToString = decodeURIComponent(
        escape(String.fromCharCode(...uint8))
    );
    // 花花天下第一美
    console.log(resToString)
});
複製代碼

ArrayBuffer

最先出現ArrayBuffer的緣由是,js要和顯卡進行數據傳輸(webGL),須要二進制數據類型。由此誕生二進制數組。web

二進制數組由三類對象組成。ajax

  • ArrayBuffer對象:表明原始的二進制數據。是內存之中的一段二進制數據,能夠經過「視圖」進行操做。「視圖」部署了數組接口,這意味着,能夠用數組的方法操做內存。經常使用的屬性byteLength,若是分配的內存區域大,須要用這個屬性檢測有沒有這樣的空間。經常使用的方法slice,拷貝生成一個新的ArrayBuffer對象。還有isView()判斷是否是下面兩種實例。express

  • TypedArray視圖:用來讀寫簡單類型的二進制數據。共包括9種類型的視圖,好比Uint8Array(無符號8位整數)數組視圖, Int16Array(16位整數)數組視圖, Float32Array(32位浮點數)數組視圖等等。json

  • DataView視圖:用來讀寫複雜類型的二進制數據。能夠自定義複合格式的視圖,好比第一個字節是 Uint8(無符號8位整數)、第2、三個字節是 Int16(16位整數)、第四個字節開始是 Float32(32位浮點數)等等,此外還能夠自定義字節序。canvas

特色:二進制數組並非真正的數組,而是相似數組的對象。注意的是,兩個視圖對應的是同一段內存,一個視圖修改底層內存,會影響到另外一個視圖。

// 建立一個8字節的ArrayBuffer,4個格子,每一個格子是一個1個字節就是8位,就是8個由0或者1組成的數,也就是最大的存儲是255,
      var buffer = new ArrayBuffer(4)
      // [0,0,0,0]
      console.log(buffer)
      // 1個格子就是8位,最大值依然255
      var x1 = new Uint8Array(buffer)
      // [0,0,0,0]
      console.log(x1)
      // 00000000 00000000 00000000 000000001 注意順序從右往左 可是數組顯示是從右往左,本身腦子繞過來[1,0,0,0]
      x1[0] = 1
      // 00000000 00000000 00000001 000000001 [1,1,0,0]
      x1[1] = 1
      // [1,1,0,0]
      console.log(x1)
      // 1個格子16位,就是佔據2份8位的格子,每一項要*256
      var x2 = new Uint16Array(buffer)
      // 0000000000000000 00000001000000001 [257,0] 這樣後面的就能表示更多的2次方,最大能256*256-1=65535
      console.log(x2)
      // 0000000000000000 00000000000000110
      x2[1] = 6
      console.log(x2)
      // 000000000000000000000000000000110 更厲害了,不贅述
      var x3 = new Uint32Array(buffer)
      // 111111111111111111111111111111111 [4294967295]
      x3[0] = 4294967295
      console.log(x3)
      // 11111111 1111111 111111 1111111 [255,255,255,255]
      console.log(x1)

      // 看看dataView new DataView(buffer [, byteOffset [, byteLength]]) 第幾個buffer開始,長度
      // view至關於 截取某段內存空間 而後以各類形式展現數據
      // 00000000【3】 00000000【2】 00000000【1】 00000000【0】
      var buffer = new ArrayBuffer(4)
      // 同上
      var view1 = new DataView(buffer)
      // 上面的左邊第一個 00000000 【3】 00000000【2】從索引2開始取2個個,也就是索引2和索引3
      var view2 = new DataView(buffer,2,2)
      // 至關於 In8Array[2] = 42 00000010【3】 00000001【2】 00000000【1】 00000000【0】
      view1.setInt8(2,1)
      view1.setInt8(3,2)
      // 至關於 In8Array[0] 00000010【3】 00000001【2】 也就是1
      console.log(view2.getInt8(0))
複製代碼

ArrayBuffer的來源:

  • new ArrayBuffer(8)
  • 從本地文件讀取,利用FileReader.readAsArrayBuffer(),開始讀取指定的 Blob(也就是file)中的內容, 一旦完成, result 屬性中保存的將是被讀取文件的 ArrayBuffer 數據對象
  • 從base64 字符串獲取
  • 經過ajax請求獲取 設置responseType 爲 ArrayBuffer 類型
// 舉個ArrayBuffer的實例吧,發送使用XMLhttpRequest發送ArrayBuffer數據:
function sendArrayBuffer() {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  var uInt8Array = new Uint8Array([1, 2, 3]);
  // 使用了類型化數組,發送的是類型化數組(uInt8Array)的buffer屬性,也就是ArrayBuffer對象。
  xhr.send(uInt8Array.buffer);
}
複製代碼

blob

  • 一個 Blob 對象表示一個不可變的,原始數據的相似文件對象。是存放二進制數據的容器。可是是相似文件對象的二進制數據。二進制數組不必定是一個文件對象。
  • blob實例有兩個屬性,size和type,size以字節爲單位,type如image/jpeg
  • File 對象是特殊類型的 Blob

blob的來源:

  • 經過new Blob(dataArr:Array<any>, options:{type:string})建立的對象就是Blob對象。dataArray能夠是二進制數組,blob,字符串,type是數組內容的MIME類型,還有slice方法
  • XMLHttpRequest裏,若是指定responseType爲blob,那麼獲得的返回值也是一個blob對象
  • canvas對象上的toBlob方法
var s = '<div>1</div>'
var blob = new Blob([s],{type:'text/html'})

var abf = new ArrayBuffer(8)
var blob = new Blob([abf])

var abv = new Unit8Array(abf)
var blob = new Blob([abv])

// slice
var blob2 = blob.slice(0,3,{type:'text/plain'})

// canvas
var canvas = document.querySelector('canvas')
canvas.toBlob(function(blob){
    var blob3 = blob
})
複製代碼

file

繼承blob。 file的來源:

  • 能夠是來自用戶在一個input元素上選擇文件後返回的FileList對象
  • 能夠是來自拖放操做生成的 DataTransfer對象
  • 能夠是來自在一個HTMLCanvasElement上執行mozGetAsFile()方法後返回結果

實踐:上傳圖片預覽的功能

<div id="containner">
      <input type="file" id="imgFile" /><br />
      <img id="previewFile" src="" height="200" alt="Image preview..." />
    </div>

    <script> const imgFile = document.querySelector("#imgFile"); const previewFile = document.querySelector("#previewFile"); imgFile.onchange = function(e) { // 類數組 file繼承blob,每一個file有不少屬性 有繼承blob的type和size,還有name,lastModified(13爲時間戳) let files = e.target.files; console.log(files); // previewImageByBlob(files[0],previewFile) previewImageByBase64(files[0], previewFile); }; function previewImageByBlob(blob, imgSelector) { // create必須後面是blob對象 const imgUrl = window.URL.createObjectURL(blob); // src能夠是根據blob建立的url 長得像這樣blob:域名/e61c67e3-df3a-453a-8f41-df740c1f5faf imgSelector.src = imgUrl; console.log(imgUrl); // 瀏覽器會在文檔退出的時候自動釋放它們,可是爲了得到最佳性能和內存使用情況,你應該在安全的時機主動釋放掉它們。 imgSelector.onload = function() { window.URL.revokeObjectURL(imgUrl); }; } function previewImageByBase64(blob, imgSelector) { var reader = new FileReader(); // readAsArrayBuffer readAsBinaryString readAsText 好幾種類型 // 讀取操做完成的時候,readyState 會變成已完成(DONE),並觸發 loadend 事件,同時 result 屬性將包含一個data:URL格式的字符串(base64編碼)以表示所讀取文件的內容,data:image/jpeg;base64,/9j/4AAQ... // 若是是服務器過來的blob,須要new Blob(blob)這樣繼續下面的操做 var x = reader.readAsDataURL(blob); console.log(x); // 必須監聽load事件,完事了纔能有完整的base64 reader.onloaded = () => { console.log("result", reader.result); imgSelector.src = reader.result; }; reader.onerror = () => { console.log("出錯了"); }; } </script>
複製代碼

Blob URL和Data URL有什麼區別呢?

  • blob顯示的形式blob:域名/e61c67e3-df3a-453a-8f41-df740c1f5faf ,dataURL的顯示形式data:image/jpeg;base64,/9j/4AAQ...
  • Blob URL的長度通常比較短,但Data URL由於直接存儲圖片base64編碼後的數據,每每很長,如上圖所示,瀏覽器在顯示Data URL時使用了省略號(…)。當顯式大圖片時,使用Blob URL能獲取更好的可能性。
  • Blob URL能夠方便的使用XMLHttpRequest獲取源數據(xhr.responseType = 'blob')。對於Data URL,並非全部瀏覽器都支持經過XMLHttpRequest獲取源數據的
  • Blob URL 只能在當前應用內部使用,把Blob URL複製到瀏覽器的地址欄中,是沒法獲取數據的。Data URL相比之下,就有很好的移植性,你能夠在任意瀏覽器中使用。
  • Blob URL除了能夠用做圖片資源的網絡地址,Blob URL也能夠用做其餘資源的網絡地址,例如html文件、json文件等,爲了保證瀏覽器能正確的解析Blob URL返回的文件類型,須要在建立Blob對象時指定相應的type

區分escape、encodeURI和encodeURIComponent

  • 編碼以後的效果是%XX或者%uXXXX這種形式
  • escape處理字符串,ASCII字母、數字、@*/+ 這幾個字符不會被編碼,其他的都會
  • encodeURIencodeURIComponent處理編碼URL,相似於https://juejin.im/editor/drafts/5cde6dae6fb9a07eda02e5f1
  • encodeURI方法不會對下列字符編碼 ,ASCII字母、數字、~!@#$&*()=:/,;?+',重點是/ ? # &
  • encodeURIComponent方法不會對下列字符編碼 ASCII字母、數字、~!*()'
  • 相應的解碼unsecape encodeURI encodeURIComponent

綜合使用,分片上傳

感受不是一時半會的,待看。重點是blob.slice 有空看這篇文章

formdata

FormData對象的做用就相似於serialize({a:1,b:2}=>a=1&b=2)方法,不過FormData是瀏覽器原生的,且支持二進制文件,是個一眼就會讓人喜歡的很讚的東西!

// 前端
var myFormData = new FormData(form容器);
// 後端 可使用multiparty來解析form-data的數據 否則原生 的那種會有不少別的文本,難以解析
app.post('/login',(req,res)=>{
    var form = new multiparty.Form();
    form.parse(req, (err, fields, files)=>{
       res.send(fields.username[0])
    });
})
複製代碼

FormData對象還有一個方法,爲append()方法,能夠人爲的給當前FormData對象添加一個鍵/值對。

myFormData.append(DOMString 鍵, Blob 值, [可選] DOMString 文件名);
myFormData.append(DOMString 鍵, DOMString 值);
// 示例 blob通常不寫的話
myFormData.append('token','2h22h2j')
複製代碼

down屬性 formData、file,arrayBuffer數據類型 arrayBuffer 預覽圖片 詳細的blob 大文件上傳 二進制文件流實現前端下載 簡單明瞭區分escape、encodeURI和encodeURIComponent

相關文章
相關標籤/搜索