用canvas的toDataURL()將圖片轉為dataURL(base64)

假設一個應用場景:由于某些特殊原因從服務端請求到圖片路徑(圖片被存儲在服務器上),要求通過該路徑獲取對應圖片的 base64 dataURL。在這個場景中,我們首先推斷該圖片路徑是可訪問的,同時還需要一種將圖片轉換到 dataURL 的方法。我們如何實現它呢?

dataURL

先大致回顧下正統的 dataURL 的語法,這有助于我們檢驗轉換后的內容是否正確。一個完整的 dataURI 應該是這樣的:

data:[<mediatype>][;base64],<data>

其中mediatype聲明了文件類型,遵循MIME規則,如“image/png”、“text/plain”;之后是編碼類型,這里我們只涉及 base64;緊接著就是文件編碼后的內容了。我們常常在 HTML 里看到img標簽的src會這樣寫:

src="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAwAAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFzByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSpa/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJlZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uisF81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PHhhx4dbgYKAAA7"

這個img引用的就是以 base64 編碼的 dataURL 了,只要瀏覽器支持,就可以被解碼成聲明格式的圖片并渲染出來。

.toDataURL()

這是一個功能函數,FileReader對象也有類似的方法,比如.readAsDataURL(),然而它只接受fileblob類型,而這兩種類型一般只能通過<input[type=file]>元素的files屬性獲取,或者用Blob()構造函數手工創建一個新的對象。尷尬的是我們當前只有圖片路徑,受制于瀏覽器的安全策略,<input[type=file]>files屬性是只讀的,而Blob()構造函數只接受文件內容,兩種方式都無法通過圖片路徑直接獲取。上文中假設的應用場景迫使我們必先考慮如何通過路徑獲取到圖片內容。<img>是可以的,并且可以被繪制到<canvas>中,而<canvas>正巧擁有.toDataURL()方法。

萬事具備,我們只需要把<img>獲取到的圖片放到<canvas>里再通過.toDataURL()方法轉化下,就可以得到以 base64 編碼的 dataURL。來看這個方法的語法:

canvas.toDataURL([type, encoderOptions]);

canvas 是 DOM 元素<canvas>對象;參數type指定圖片類型,如果指定的類型不被支持則以默認值image/png替代;encoderOptions可以為image/jpegimage/webp類型的圖片設置圖片質量,取值0-1,超出則以默認值0.92替代。

需要注意的是,圖片加載是異步的,在轉換成 dataURL 前必須先確保圖片成功加載到,否則讓 canvas 即刻執行繪制可能失敗,從而導致轉換 dataURL 失敗。于是.toDataURL()方法應該寫在<img>onload事件中,以確保 canvas 的繪制工作在圖片下載完成后開始。另一個問題是<img>圖片渲染到<canvas>上也需要一個過程,好在.drawImage()方法是同步的,只有在 canvas 繪制完成后才會執行后續如.toDataURL()的操作。現在就來實現一個功能函數:

function getBase64(url){
  // 通過構造函數來創建的 img 實例
  // 在賦予 src 值后就會立刻下載圖片
  // 相比 createElement() 創建 <img> 省去了 append(),也就避免了文檔冗余和污染
  let dataURL = ''
  let img = new Image();

  img.src = url;
  img.onload = () => { // 要先確保圖片完整獲取到,這是個異步處理
    let canvas = document.createElement('canvas'); // 創建canvas元素
    let [width,height] = [img.width,img.height]; // 確保canvas的尺寸和圖片一樣
    canvas.width = width;
    canvas.height = height;
    canvas.getContext('2d').drawImage(img, 0, 0, width, height); // 將圖片繪制到canvas中
    dataURL = canvas.toDataURL('image/jpeg'); // 轉換圖片為dataURL
  }
}

一個可供隨時調用的轉換函數完成了,它會在圖片被加載后返回一整個 dataURL 字符串。

完善

onload事件確保了轉換任務在圖片加載后執行,卻又帶來了新問題——dataURL 只有在圖片加載完成后才會返回,我們是無法精準確定圖片完成加載的時間的。如果后續要對 dataURL 做相關處理(比如傳遞到其他服務器)的話,添加一個回調是必要的,這能確保后續處理任務在成功得到 dataURL 之后執行,我們修改一下getBase64()

function getBase64(url, callback){ //添加一個回調參數
  ...
  img.onload = () => {
    ...
    canvas.getContext('2d').drawImage(img, 0, 0, width, height);
    dataURL=canvas.toDataURL('image/jpeg');
    callback&&callback(dataURL); //調用回調函數
  }
}

在執行時添加回調:

let imgURL = '//upload.jianshu.io/users/upload_avatars/555630/fdd1b798e6b0.jpg';

getBase64(imgURL, dataURL => {
  console.log(dataURL)
});

就是這樣。如果不考慮兼容性的話,或許我們可以用 promise 和 generator 來實現,再添加一些錯誤處理就更完美了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容

  • 一、canvas簡介 1.1 什么是canvas?(了解) 是HTML5提供的一種新標簽 Canvas是一個矩形區...
    J_L_L閱讀 1,528評論 0 4
  • Swift1> Swift和OC的區別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,120評論 1 32
  • 在項目中,需要生成海報。有動態信息(微信頭像、微信昵稱、上傳圖片(oss鏈接)、二維碼)+ 海報背景圖生成一張海報...
    奔跑吧笨笨閱讀 7,715評論 0 0
  • 旋轉壓縮部分因為有文件的不同類型相互轉換,所以方法調用比較多,看起來有點亂,但是實際不難理解,建議粘貼到編輯器比較...
    名字_d880閱讀 3,247評論 0 1
  • 一、圖形的組合方式 globalAlpha是一個介于0和1之間的值(包括0和1),用于指定所有繪制的透明度。默認值...
    空谷悠閱讀 1,295評論 0 0