欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

js獲取圖片base64的正確實(shí)現(xiàn)方式

 更新時間:2024年01月03日 16:27:14   作者:wtcl_wtcl  
這篇文章主要給大家介紹了關(guān)于js獲取圖片base64的正確實(shí)現(xiàn)方式,BLOB是二進(jìn)制大對象,是一個可以存儲二進(jìn)制文件的容器,?在計(jì)算機(jī)中BLOB常常是數(shù)據(jù)庫中用來存儲二進(jìn)制文件的字段類型,需要的朋友可以參考下

前言

最近遇到了一個需求,要求在python的selenium自動化打開的瀏覽器中,執(zhí)行js代碼,實(shí)現(xiàn)獲取圖片的base64,并返回給python變量。

看似很簡單的一個需求,實(shí)際上包含了很多js相關(guān)的知識點(diǎn)和開發(fā)上的技術(shù)點(diǎn)。

今天這篇文章,主要寫寫整個過程中有用的知識點(diǎn)。

原始方法

其實(shí)最開始我沒有考慮用js獲取圖片,所以用pyautogui鍵盤操作下載圖片,代碼如下:

import pyautogui
from selenium.webdriver import ActionChains

driver.get(url)
ActionChains(driver).move_to_element(driver.find_element_by_tag_name('img')).context_click().perform()
pyautogui.typewrite(['v'])
time.sleep(1)
pyautogui.typewrite(['enter'])

這里面,主要運(yùn)用電腦上的快捷鍵,在選擇圖片后,右鍵,然后按v鍵,就會彈出保存窗口,再按enter,就可以按照默認(rèn)信息保存。

這種方式是模擬手動,原理簡單,但是,在多次重復(fù)這個操作時,會發(fā)現(xiàn)偶爾會出現(xiàn)一個bug,就是按v鍵會與保存沖突,使得電腦以為將圖片名命名為v,然后整個過程就無法繼續(xù)進(jìn)行了。

為了解決上述問題,我才采用js的方法,才有了后面的內(nèi)容。

第一版js代碼

var source = document.getElementsByTagName('img')[0];
// 在網(wǎng)頁元素中找到圖片資源
var img=new Image();
// 創(chuàng)建一個image元素
img.src=source.src;
// 設(shè)置img的圖片鏈接為網(wǎng)頁圖片鏈接
img.crossOrigin='anonymous';
// 設(shè)置跨域的配置,這個是比較常見的,如果不了解,讀者可以自行百度
var xhr=new XMLHttpRequest();
// 初始化一個http請求
xhr.open('GET',source.src,false);
// 構(gòu)造請求
xhr.send(null);
// 發(fā)送請求
xhr.open('GET',source.src,false); // 重復(fù)上面步驟,為了確定加載到資源
xhr.send(null);
var canvas = document.createElement('canvas');  
// 創(chuàng)建canvas元素
var ctx=canvas.getContext('2d');
// 獲取canvas二維屬性
ctx.drawImage(img,0,0);
// 將img畫到canvas上
var dataURL = canvas.toDataURL('image/png');
// 利用canvas轉(zhuǎn)換圖片為base64形式
return dataURL;

上面的代碼運(yùn)行后,發(fā)現(xiàn)獲取到的base64還原出的圖片是黑色的,運(yùn)行很多次都這樣。所以我又在瀏覽器console下一條一條執(zhí)行,結(jié)果發(fā)現(xiàn)能夠給出正常的base64,但是在瀏覽器直接解析后,發(fā)現(xiàn)圖片雖然能夠正常顯示,但是圖片的大小是被裁剪的。

仔細(xì)思考后,我感覺可能是因?yàn)楂@取的圖片的問題,但是看了network的加載記錄,發(fā)現(xiàn)好像并不是這樣,加載的圖片都是完整的。所以,可能是canvas畫圖的時候出了一些問題,所以就去了解canvas的drawImage函數(shù)。

研究一番,發(fā)現(xiàn),這個函數(shù)第一個參數(shù)是圖片信息,第二和三個參數(shù)是畫布的坐標(biāo),表示從哪個位置開始畫,然后還可以有第四和五個參數(shù),表示要畫出的圖的寬和高,所以我就修改了那句為

ctx.drawImage(img,0,0,source.naturalWidth,source.naturalHeight);
// 這里面的source是指網(wǎng)頁的圖片,然后naturalWidth是原寬,naturalHeight是原高

這里就把整個圖畫上去,但是經(jīng)過實(shí)驗(yàn)后,我發(fā)現(xiàn)畫出來的圖還是裁剪后的圖,而且,每次的圖的大小都是一樣,這就使我明白,會不會是畫布的大小出了問題。

經(jīng)過查閱資料,我發(fā)現(xiàn)canvas是有初始大小的,而且如果不改的話,是不會變的。所以,我又在畫圖前設(shè)置canvas的畫布大小。

canvas.width=source.naturalWidth;
canvas.height=source.naturalHeight;

經(jīng)過這次,我在瀏覽器端一條一條執(zhí)行時,發(fā)現(xiàn)代碼執(zhí)行沒問題,最后結(jié)果都正確。我以為第一天的工作結(jié)束了,解決了這個問題,明天簡單弄弄就好了,但是一個更麻煩的問題在路上了。

注意:上面的在console下一條一條執(zhí)行代碼和在python中執(zhí)行js代碼的結(jié)果是不一樣的。因?yàn)橐粭l一條執(zhí)行,中間是有時間消耗的,不好異步任務(wù)可以在此期間完成。

第二版js代碼

var source = document.getElementsByTagName('img')[0];
// 在網(wǎng)頁元素中找到圖片資源
var img=new Image();
// 創(chuàng)建一個image元素
img.src=source.src;
// 設(shè)置img的圖片鏈接為網(wǎng)頁圖片鏈接
img.crossOrigin='anonymous';
// 設(shè)置跨域的配置,這個是比較常見的,如果不了解,讀者可以自行百度
var xhr=new XMLHttpRequest();
// 初始化一個http請求
xhr.open('GET',source.src,false);
// 構(gòu)造請求
xhr.send(null);
// 發(fā)送請求
xhr.open('GET',source.src,false); // 重復(fù)上面步驟,為了確定加載到資源
xhr.send(null);
var canvas = document.createElement('canvas');  
// 創(chuàng)建canvas元素
canvas.width=source.naturalWidth;
// 設(shè)置畫布寬
canvas.height=source.naturalHeight;
// 設(shè)置畫布高
var ctx=canvas.getContext('2d');
// 獲取canvas二維屬性
ctx.drawImage(img,0,0,source.naturalWidth,source.naturalHeight);
// 將img畫到canvas上
var dataURL = canvas.toDataURL('image/png');
// 利用canvas轉(zhuǎn)換圖片為base64形式
return dataURL;

上面代碼是前面修改后、我以為正確的代碼,但是,第二天放到python中運(yùn)行后,發(fā)現(xiàn)結(jié)果還是不對,圖片仍然是黑色的,不正確。

我說,難道是我保存的昨天的代碼不對嗎?所以還是重復(fù)錯誤的步驟,在瀏覽器下一條一條的運(yùn)行。結(jié)果是正確的,所以我就很納悶,一樣的代碼怎么會有不同的結(jié)果?

我不理解,但是我感覺可以試試將代碼包裝從函數(shù)的形式,在瀏覽器端運(yùn)行。觀察一下結(jié)果,發(fā)現(xiàn)結(jié)果給出的內(nèi)容確實(shí)不是圖片正常的base64,只能轉(zhuǎn)為黑色的圖片。

為了驗(yàn)證我的結(jié)果,我還運(yùn)行了多次,結(jié)果都一樣。我還將瀏覽器的正常結(jié)果、錯誤結(jié)果和python端的結(jié)果進(jìn)行對比,我發(fā)現(xiàn)正確結(jié)果是很長的,而兩個錯誤結(jié)果一樣長度,都很短。

故事到這,陷入了僵局,我不知道該怎么辦了。

正好到晚飯點(diǎn)了,所以就先去吃飯吧,吃飯的過程中,看看canvas的文檔,了解一下具體的使用方法,說不定會有一些意外的收獲。

在晚飯期間,我看到canvas畫圖的一些注意事項(xiàng):若調(diào)用 drawImage 時,圖片沒裝載完,那什么都不會發(fā)生(在一些舊的瀏覽器中可能會拋出異常)。因此你應(yīng)該用 load 事件來保證不會在加載完畢之前使用這個圖片。(來源于https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Using_images)

官方文檔給出了案例:

var img = new Image();   // 創(chuàng)建 img 元素
img.src = 'myImage.png'; // 設(shè)置圖片源地址
img.onload = function(){
  // 執(zhí)行 drawImage 語句
}

我沒有直接將這個作為解決方案,我又多看了一些論壇,發(fā)現(xiàn)大部分都是這樣實(shí)現(xiàn)的。

后面,將這個代碼加到我的代碼中,我發(fā)現(xiàn)return語句在外面的話,還是會返回錯值;return語句里面的話,是可以正常執(zhí)行的,但是返回的值是無法返回到外層的。

于是,我靈機(jī)一動,直接return img.onload…

在瀏覽器下確實(shí)成功了,但是,在python這邊,卻還不對,返回的是一個函數(shù)對象。

到此,我明白,要等圖片裝載完成后,再執(zhí)行drawimage語句,然后再返回才行。但是,js代碼不會等,只要執(zhí)行了就會繼續(xù)往下執(zhí)行下一條,不管你是否執(zhí)行完成。

第三版js代碼

為了解決加載完成的問題,我在想是不是有可以休眠等待的函數(shù),查閱之后,發(fā)現(xiàn)可以自己構(gòu)造一個sleep函數(shù)。

const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay));
sleep(1000); // 休眠1s

但是,加在上面代碼后面,返回結(jié)果還是那樣,感覺無法停止代碼執(zhí)行。

經(jīng)過查閱資料,發(fā)現(xiàn)還有一個setTimeout函數(shù),可以設(shè)置一段時間后再執(zhí)行函數(shù),但是輸入值只能是函數(shù),這就要求在函數(shù)里面返回最后的base64。但是,實(shí)際實(shí)驗(yàn)了一下,發(fā)現(xiàn)這個方式行不通,不返回內(nèi)容。

然后,繼續(xù)查閱資料,發(fā)現(xiàn)可以通過throw error拋出內(nèi)容,我當(dāng)然也嘗試了,發(fā)現(xiàn)都是uncaught。我當(dāng)時就納悶,然后把setTimeout函數(shù)也換成了img.onload,但是結(jié)果都不對。

中間經(jīng)過大量的實(shí)驗(yàn),省略n次嘗試。。。

最后,我明白了只有在try里面直接執(zhí)行throw error代碼,才能夠catch到。至于利用try包裹etTimeout和img.load等耗時異步語句,都不能正常catch,因?yàn)檎?zhí)行到throw error時,已經(jīng)結(jié)束了外面了try{}catch{}語句的執(zhí)行,所以根本catch不到。

上面的一大堆過程,讓我明白了,根本原因是在異步代碼的執(zhí)行完成前能否不繼續(xù)執(zhí)行下面的代碼。

最終的js代碼

var source = document.getElementsByTagName('img')[0];
var img=new Image();
img.src=source.src;
img.crossOrigin='anonymous';
var canvas = document.createElement('canvas');
canvas.width=source.naturalWidth;
canvas.height=source.naturalHeight;
var ctx=canvas.getContext('2d');
function httpPromise(){
   return new Promise((resolve,reject) => {
       if(img.complete || img.naturalHeight!==0)
       {
         ctx.drawImage(img,0,0,source.naturalWidth,source.naturalHeight);
         var dataURL = canvas.toDataURL('image/png');
         resolve(dataURL);
        }
        else{
         img.onload=function(){ 
                ctx.drawImage(img,0,0,source.naturalWidth,source.naturalHeight);
                var dataURL = canvas.toDataURL('image/png');
                resolve(dataURL);
               }
         }
    })
}
return httpPromise().then((dataURL)=>{return dataURL;}

最后這版代碼做了一些改動,首先我刪除了xhr加載的代碼,因?yàn)檫@個加載過程和最后的結(jié)果沒有關(guān)系,不影響最后的結(jié)果;然后,我添加了Promise異步處理機(jī)制,運(yùn)用這個,就可以等待異步執(zhí)行結(jié)束后,收集所有的結(jié)果,并且返回(我記得java也有類似的庫可以解決異步等待的問題);最后,我添加了判斷機(jī)制,防止img.onload因?yàn)榫彺娴脑虿豁憫?yīng),所以分成了兩種情況進(jìn)行處理,但都是將結(jié)果收集,在return語句處返回。

插一嘴:這里需要return語句,是因?yàn)閜ython里面的driver.excute_script函數(shù)是將我們的js代碼包裝成函數(shù),需要返回內(nèi)容進(jìn)行處理,否則返回值就直接為None。

上面就是我通過自己摸索,實(shí)現(xiàn)的這樣一個需求,雖然經(jīng)過了很多磕磕絆絆,但是結(jié)果還是好的。其實(shí),有一篇博客的代碼我覺得是優(yōu)于我的代碼。

大佬的代碼

// https://blog.csdn.net/yaxuan88521/article/details/122794055
let c = document.createElement('canvas');
let ctx = c.getContext('2d');
let img = document.getElementsByTagName('img')[0];
c.height=img.naturalHeight;
c.width=img.naturalWidth;
ctx.drawImage(img, 0, 0,img.naturalWidth, img.naturalHeight);
let base64String = c.toDataURL();
return base64String;

上面大佬的代碼,是直接獲取加載后的圖片元素,效果是優(yōu)于我的代碼,畢竟減少了一次加載的時間消耗。

總結(jié)

本文通過筆者在js獲取圖片base64的過程中,遇到的問題。
總的說來,可以分為以下幾點(diǎn):

  • canvas繪圖需要修改畫布大小,否則無法畫完整。
  • canvas的drawimage函數(shù),需要等待圖片加載完成,否則就會直接返回畫布。
  • js代碼本身是同步執(zhí)行的,但是如果實(shí)現(xiàn)了一些異步函數(shù),如setTimeout和img.onload等,可能會出現(xiàn)返回結(jié)果不正確的情況,所以,如果需要返回值,最好利用funture構(gòu)造,最后接收返回值。
  • 對于同一個需求,最好選擇更為方便的方法實(shí)現(xiàn),否則會浪費(fèi)時間和資源。(筆者在這個地方花費(fèi)時間有點(diǎn)多,但是對于js也有了更深的認(rèn)識)

最后,本文主要介紹了js獲取圖片base64的問題,中間過程有些曲折,內(nèi)容可能有點(diǎn)凌亂,但是技術(shù)點(diǎn)還是給出了,希望各位讀者能夠從中有所收獲。

附:js實(shí)現(xiàn)base64格式編碼圖片(通俗,易懂)

vip會員內(nèi)容

相關(guān)文章

  • 微信小程序錄音與播放錄音功能

    微信小程序錄音與播放錄音功能

    這篇文章主要介紹了微信小程序錄音與播放錄音功能,小程序中提供了兩種錄音的API,舊版本錄音功能和新版錄音功能,需要的朋友可以參考下
    2017-12-12
  • 第六章之輔組類與響應(yīng)式工具

    第六章之輔組類與響應(yīng)式工具

    Bootstrap,來自 Twitter,是目前最受歡迎的前端框架。本文給大家介紹BootStrap組件之輔組類與響應(yīng)式工具,感興趣的朋友一起學(xué)習(xí)吧
    2016-04-04
  • JavaScript實(shí)現(xiàn)拖拽元素對齊到網(wǎng)格(每次移動固定距離)

    JavaScript實(shí)現(xiàn)拖拽元素對齊到網(wǎng)格(每次移動固定距離)

    最近在做一個拖拽元素的附加功能,就是對齊到網(wǎng)格,實(shí)際上就是確定好元素的初始位置,然后拖拽元素時,每次移動固定的距離。讓元素都可以在網(wǎng)格內(nèi)對齊
    2016-11-11
  • JS實(shí)現(xiàn)下拉菜單列表與登錄注冊彈窗效果

    JS實(shí)現(xiàn)下拉菜單列表與登錄注冊彈窗效果

    下面小編就為大家?guī)硪黄狫S實(shí)現(xiàn)下拉菜單列表與登錄注冊彈窗效果。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 詳解ES6中class的實(shí)現(xiàn)原理

    詳解ES6中class的實(shí)現(xiàn)原理

    這篇文章主要介紹了詳解ES6中class的實(shí)現(xiàn)原理,幫助大家更好的理解和學(xué)習(xí)JavaScript es6,感興趣的朋友可以了解下
    2020-10-10
  • 微信小程序連接服務(wù)器展示MQTT數(shù)據(jù)信息的實(shí)現(xiàn)

    微信小程序連接服務(wù)器展示MQTT數(shù)據(jù)信息的實(shí)現(xiàn)

    這篇文章主要介紹了微信小程序連接服務(wù)器展示MQTT數(shù)據(jù)信息的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • js下將金額數(shù)字每三位一逗號分隔

    js下將金額數(shù)字每三位一逗號分隔

    這篇文章主要介紹了js下將金額數(shù)字每三位一逗號分隔的相關(guān)資料,還附加了一個小功能,小數(shù)位保留兩位,感興趣的小伙伴們可以參考一下
    2016-02-02
  • JavaScript實(shí)現(xiàn)日期格式化的操作詳解

    JavaScript實(shí)現(xiàn)日期格式化的操作詳解

    在我們做業(yè)務(wù)開發(fā)的漫長歲月里,會多次跟時間打交道,相信大多數(shù)小伙伴對日期格式化也并不陌生,本文簡單記錄了JavaScript實(shí)現(xiàn)日期格式化的過程,以及一些拓展,希望對大家有所幫助
    2023-05-05
  • js觸發(fā)select onchange事件的小技巧

    js觸發(fā)select onchange事件的小技巧

    select 或text的onchange事件需要手動改變select或text的值才能觸發(fā),下面有個不錯的方法可以通過js觸發(fā)select onchange事件
    2014-08-08
  • JS前端導(dǎo)出Excel的方法詳解

    JS前端導(dǎo)出Excel的方法詳解

    最近寫管理端的需求,發(fā)現(xiàn)有一個excel導(dǎo)出的需求,所以正好本文就來和大家分享一下導(dǎo)出excel文件的三種實(shí)現(xiàn)方式,感興趣的小伙伴可以了解一下
    2023-07-07

最新評論