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

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

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

前言

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

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

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

原始方法

其實(shí)最開始我沒(méi)有考慮用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鍵,就會(huì)彈出保存窗口,再按enter,就可以按照默認(rèn)信息保存。

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

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

第一版js代碼

var source = document.getElementsByTagName('img')[0];
// 在網(wǎng)頁(yè)元素中找到圖片資源
var img=new Image();
// 創(chuàng)建一個(gè)image元素
img.src=source.src;
// 設(shè)置img的圖片鏈接為網(wǎng)頁(yè)圖片鏈接
img.crossOrigin='anonymous';
// 設(shè)置跨域的配置,這個(gè)是比較常見的,如果不了解,讀者可以自行百度
var xhr=new XMLHttpRequest();
// 初始化一個(gè)http請(qǐng)求
xhr.open('GET',source.src,false);
// 構(gòu)造請(qǐng)求
xhr.send(null);
// 發(fā)送請(qǐng)求
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ì)思考后,我感覺(jué)可能是因?yàn)楂@取的圖片的問(wèn)題,但是看了network的加載記錄,發(fā)現(xiàn)好像并不是這樣,加載的圖片都是完整的。所以,可能是canvas畫圖的時(shí)候出了一些問(wèn)題,所以就去了解canvas的drawImage函數(shù)。

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

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

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

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

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

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

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

第二版js代碼

var source = document.getElementsByTagName('img')[0];
// 在網(wǎng)頁(yè)元素中找到圖片資源
var img=new Image();
// 創(chuàng)建一個(gè)image元素
img.src=source.src;
// 設(shè)置img的圖片鏈接為網(wǎng)頁(yè)圖片鏈接
img.crossOrigin='anonymous';
// 設(shè)置跨域的配置,這個(gè)是比較常見的,如果不了解,讀者可以自行百度
var xhr=new XMLHttpRequest();
// 初始化一個(gè)http請(qǐng)求
xhr.open('GET',source.src,false);
// 構(gòu)造請(qǐng)求
xhr.send(null);
// 發(fā)送請(qǐng)求
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é)果還是不對(duì),圖片仍然是黑色的,不正確。

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

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

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

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

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

在晚飯期間,我看到canvas畫圖的一些注意事項(xiàng):若調(diào)用 drawImage 時(shí),圖片沒(méi)裝載完,那什么都不會(huì)發(fā)生(在一些舊的瀏覽器中可能會(huì)拋出異常)。因此你應(yīng)該用 load 事件來(lái)保證不會(huì)在加載完畢之前使用這個(gè)圖片。(來(lái)源于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 語(yǔ)句
}

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

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

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

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

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

第三版js代碼

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

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

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

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

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

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

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

上面的一大堆過(guò)程,讓我明白了,根本原因是在異步代碼的執(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;}

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

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

上面就是我通過(guò)自己摸索,實(shí)現(xiàn)的這樣一個(gè)需求,雖然經(jīng)過(guò)了很多磕磕絆絆,但是結(jié)果還是好的。其實(shí),有一篇博客的代碼我覺(jué)得是優(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)于我的代碼,畢竟減少了一次加載的時(shí)間消耗。

總結(jié)

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

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

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

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

vip會(huì)員內(nèi)容

相關(guān)文章

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

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

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

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

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

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

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

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

    下面小編就為大家?guī)?lái)一篇JS實(shí)現(xiàn)下拉菜單列表與登錄注冊(cè)彈窗效果。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    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),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • js下將金額數(shù)字每三位一逗號(hào)分隔

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

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

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

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

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

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

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

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

最新評(píng)論