做者: gauseen
原文: https://github.com/gauseen/blog
公衆號【學前端】只搞技術,不搞廣告文,肯定不關注一下?
相信在工做中常常遇到,文件上傳、圖片壓縮、文件下載、大文件斷點續傳,等等關於 js 來操做文件的需求。那麼你真的瞭解文件類型之間的轉換關係嗎?以下:javascript
Blob
--> File
File
--> DataURL(base64)
File
--> BlobURL
HTTPURL| DataURL | BlobURL
--> Blob
提示: 公衆號回覆 「file」 可得高清原圖html
Blob
類型是 File
文件類型的父類,它表示一個不可變、原始數據的類文件對象前端
blob
對象?1. new Blob(array, options)java
let hiBlob = new Blob([`<h1>Hi gauseen!<h1>`], { type: 'text/html' })
如上代碼,就建立了一個 blob
對象,並聲明瞭 text/html
類型 ,就像是建立一個 .html
文件。只不過它存在於瀏覽器的內存裏。jquery
2. fetch(url)nginx
js 爲咱們提供了不少獲取資源的 api,如:<img> 和 <script>
,Fetch API
提供了一個獲取資源的統一接口(包括跨域請求)git
關於 fetch(url, options)
, url
參數支持格式有:github
截止到 2020-01-13
http、https
blobURL
: 好比經過 URL.createObjectURL()
得到web
// blobURL 示例: blob:null/7025638d-c05f-4c75-87d6-470a427e9aa3
dataURL
: 如圖片的 base64 格式,好比經過 convasElement.toDataURL()
得到json
// dataURL(base64) 黑色 1 像素示例: data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=
fetch(url, options)
響應數據可被解析成:
res.arrayBuffer()
: 通用、固定長度的原始二進制數據緩衝區res.blob()
: Blob
類型res.formData()
: 表單數據類型res.json()
: JSON
格式res.text()
: 文本格式本文主要關心 blob
類型轉換,以下代碼,用 fetch api 獲取圖片資源的 blob 對象,
固然也能夠獲取其它類型的資源。如:.txt
.html
等
// 獲取圖片的 blob 對象 // 經過 http、https 獲取 fetch('http://eg.com/to/path/someImg.png') .then(res => res.blob()) .then(blob => { console.log('blob: ', blob) })
3. canvasElement.toBlob(callback)
canvas 具備圖像操做能力,支持將一個已有的圖片做爲圖片源,來操做圖像。
以下,經過 canvas 將圖片資源轉成 blob
對象
<body> <canvas width="100" height="100"></canvas> </body> <script> const $ = arg => document.querySelector(arg) let convas = $('canvas') // async 自執行函數 (async () => { let imgUrl = 'http://eg.com/to/path/someImg.png' let ctx = convas.getContext('2d') let img = await fetchImg(imgUrl) // 向 canvas 畫布上下文繪製圖片 ctx.drawImage(img, 0, 0) // 獲取圖片 blob 對象 convas.toBlob((blob) => { console.log('blob: ', blob) }) // 獲取圖片 dataURL,也是 base64 格式 let dataURL = convas.toDataURL() console.log('dataURL: ', dataURL) })() // 獲取圖片資源,封裝成 promise function fetchImg (url) { return new Promise((resolve, reject) => { let img = new Image() // 跨域圖片處理 img.crossOrigin = 'anonymous' img.src = url // 圖片資源加載完成回調 img.onload = () => { resolve(img) } }) } </script>
注:
drawImage
,canvas 繪製將失敗,因此咱們簡單封裝了 fetchImg
方法,確保圖片資源加載完成後再開始繪製圖片。因爲 canvas 中的圖片可能來自一些第三方網站。在不作處理的狀況下,使用跨域的圖片繪製時會污染畫布,這是出於安全考慮。在「被污染」的畫布中調用 toBlob()
toDataURL()
getImageData()
會拋出安全警告。
解決方法:
let img = new Image() // 1. 增長 crossOrigin 屬性,值爲 anonymous // 含義:執行一個跨域請求,在請求頭裏加 origin 字段 // 2. 後端要返回 Access-Control-Allow-Origin 響應頭來容許跨域 img.crossOrigin = 'anonymous' img.src = 'to/path'
本質就是解決跨域問題,也可使用 nginx
作個代理來解決
blob
有 slice(startIndex, endIndex)
方法,複製 blob 對象某片斷,與 js 數組的 slice
方法相似,文件的斷點續傳功能就是利用了改特性。File
包含文件的相關信息,能夠經過 js 來訪問其內容
file
對象?1. new File(bits, name[, options])
// 1. 參數是字符串組成的數組 let hiFile = new File([`<h1>Hi gauseen!<h1>`], 'fileName', { type: 'text/html' }) // 2. blob 轉 file 類型 let hiBlob = new Blob([`<h1>Hi gauseen!<h1>`], { type: 'text/html' }) let hiFile = new File([ hiBlob ], 'fileName', { type: 'text/html' })
如上代碼,經過 File
構造函數,建立一個 file
對象,與上面的提到的 blob
相似。能夠將 blob 轉成 file 類型,這意味着上面獲取的 blob,能夠轉成 file 類型。
2. inputElement.files
經過 <input type="file">
標籤獲取 file
對象
// input 上傳文件時觸發 change 事件 $('input').addEventListener('change', e => { let file = e.target.files[0] console.log('file: ', file) })
3. DragEvent.dataTransfer.files
經過拖、放獲取 file
對象
<body> <div id="output"> 將文件拖放到這裏~ </div> </body> <script> const $ = arg => document.querySelector(arg) let outputEle = $('#output') // ondragover 事件規定在何處放置被拖動的數據 outputEle.addEventListener('dragover', dragEvent => { dragEvent.preventDefault() }) // ondrop 事件放置文件時觸發 outputEle.addEventListener('drop', dragEvent => { dragEvent.preventDefault() // DataEvent.dataTransfer 屬性保存着拖拽操做中的數據 let files = dragEvent.dataTransfer.files console.log('drag files: ', files) }) </script>
4. canvasElement.mozGetAsFile()
注: 截止 2020-01-13
,該方法僅支持火狐瀏覽器
let file = canvasElement.mozGetAsFile('imgName')
DataURL,前綴爲 data:
協議的 URL,能夠存儲一些小型數據
語法:data:[<mediatype>][;base64],<data>
以下,黑色 1 像素示例:
data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=
上面提到的 Blob
File
類型,如何「消費」它們呢?接着向下看
1. FileReader
容許 Web 應用程序異步讀取存儲在用戶計算機上的文件(blob
或 file
)。
// 將 blob 或 file 轉成 DataURL(base64) 形式 fileReader(someFile).then(base64 => { console.log('base64: ', base64) }) function fileReader (blob) { return new Promise((resolve, reject) => { let reader = new FileReader() reader.onload = (e) => { resolve(e.target.result) } reader.readAsDataURL(blob) }) }
2. convasElement.toDataURL()
能夠經過 canvas 圖像處理能力,將圖片轉成 dataURL 形式。在上面 Blob 部分講解中,代碼已實現。
BlobURL
也叫 ObjectURL
,它可讓只支持 URL 協議的 Api(如:<a> <link> <img> <script>
) 訪問 file
或 blob
對象。
dynamic-import-polyfill 庫也用到了其特性。
以下,生成 blobURL
,createObjectURL
方法建立從 URL 到 Blob 的映射關係。
如:blob:http://eg.com/550e8400-e29b-41d4-a716-446655440000
// object 建立 URL 的 File 對象、Blob 對象或者 MediaSource 對象 let blobURL = URL.createObjectURL(object)
以下,revokeObjectURL
方法撤消 blobURL 與 Blob 的映射關係,有助於瀏覽器垃圾回收,提示性能。
URL.revokeObjectURL(blobURL)
經過上面的一系列轉換關係,能夠知道:
blob --> file --> dataURL(base64) | blobURL --> blob
這樣就造成了一個閉環,文章開頭的思惟導圖很好的說明了之間的轉換關係。
經過 a 標籤實現下載,blob 或 file 對象。至於如何獲取 blob 和 file 上面已經說得很清楚了。
function downloadFile1 (blob, fileName = 'fileName') { let blobURL = URL.createObjectURL(blob) let link = document.createElement('a') link.download = fileName link.href = blobURL link.click() // 釋放 blobURL URL.revokeObjectURL(blobURL) } // 固然也能夠經過 window.location.href 下載文件 function downloadFile2 (blob, fileName = 'fileName') { let blobURL = URL.createObjectURL(blob) window.location.href = blobURL // 釋放 blobURL URL.revokeObjectURL(blobURL) }
// 壓縮圖片,圖片質量爲 0.6 compressImage('to/path/someImg.png', 0.6).then(base64 => { console.log('compressImage: ', base64) }) // imgUrl 圖片地址 // quality 圖片質量 0 ~ 1 之間 // type 壓縮圖片只支持,image/jpeg 或 image/webp 類型 // 返回 base64 數據 async function compressImage (imgUrl, quality = 1, type = 'image/jpeg') { let imgEle = await fetchImg(imgUrl) let canvas = document.createElement('canvas') let cxt = canvas.getContext('2d') // 設置 canvas 寬高 let { width, height } = imgEle canvas.setAttribute('width', width || 100) canvas.setAttribute('height', height || 100) cxt.drawImage(imgEle, 0, 0) return canvas.toDataURL(type, quality) } // 經過 url 獲取圖片 function fetchImg (url) { return new Promise((resolve, reject) => { let img = new Image() img.crossOrigin = 'Anonymous' img.src = url img.onload = () => { resolve(img) } }) }
相信讀完這篇文章之後,你會對文件類型之間的轉換有更全方位的瞭解,其實還有不少像 ArrayBuffer
存儲二進制數據相關的 Api 沒有寫到,由於平時用到的場景比較少,感興趣的能夠結合本文,去更深一步的探索。
歡迎關注無廣告文章公衆號:學前端
PS: 只搞技術不搞廣告文都不關注?沒天理啦!
你的關注是我更文最大動力!