JavaScript實(shí)現(xiàn)獲取圖片文件真實(shí)格式的示例代碼
目錄
- 常見方式判斷圖片格式
- 圖像數(shù)據(jù)簡單說明
- JS讀取圖片真實(shí)格式
- svg格式的判斷
- 總結(jié)
前面博文有提到,當(dāng)前主流瀏覽器能支持的圖片格式,是七種:jpg、png、gif、bmp、ico、webp、svg,其中,前六種都是位圖,svg則是唯一的矢量圖。
每種格式的圖片,都有自己特有的優(yōu)缺點(diǎn)以及數(shù)據(jù)結(jié)構(gòu),本篇博文的目的就是基于不同格式的圖像二進(jìn)制數(shù)據(jù),獲取到圖片的真實(shí)格式。
常見方式判斷圖片格式
當(dāng)我們進(jìn)行前端開發(fā),需要處理圖片上傳功能,針對圖片格式做判斷時,常規(guī)的方法都是使用文件后綴名來判斷,如下代碼所示:
input.addEventListener("change", (e) => { const file = e.target.files[0] const format = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()}, false)
以上代碼,監(jiān)聽上傳控件的事件,得到要上傳的文件信息,獲取文件名稱,然后通過獲取文件名稱截取文件后綴名,以后綴名作為圖片文件的格式。
這段代碼,大部分人都比較熟悉,也有很多場景下是這樣來判斷圖片格式的,但如果我們強(qiáng)行修改了文件的后綴名,則此方法就失效了。
我們知道gif格式圖片的位深度是8,如果我們強(qiáng)制把位深度為32的png格式的圖片后綴名改成gif,這個圖片文件依然可以正常使用:
上圖所示,就是將png格式文件后綴名改成了gif,圖片系統(tǒng)信息顯示格式為gif,但是位深度還是32,圖像本質(zhì)上還是png格式的。
這個時候,單純的通過后綴名來判斷圖片的格式,就不再準(zhǔn)確了,我們需要另外的方式來獲取圖片文件的真實(shí)格式。而這種方式就需要使用到前端二進(jìn)制相關(guān)的知識,見前文介紹深入理解前端字節(jié)二進(jìn)制知識以及相關(guān)API。
修改后綴名的方式
- 幾種位圖格式之間,是可以相互修改后綴名,圖片仍能正常使用
- gif動圖后綴名改成其他位圖格式,則動效會失效,變成靜態(tài)圖
- 位圖格式的后綴名如果改成矢量圖svg,則圖片失效,將無法使用
- svg圖片文件后綴名改成位圖格式,圖片也將無法使用
圖像數(shù)據(jù)簡單說明
不同格式的圖像所存儲的數(shù)據(jù)是不一樣的,都有自己特殊的數(shù)據(jù)結(jié)構(gòu)。
依據(jù)各個格式圖像不同的數(shù)據(jù)結(jié)構(gòu),我們通過類型數(shù)組中的圖像數(shù)據(jù),就能判斷出圖片的真實(shí)格式。
- 如jpg格式,它的圖像數(shù)據(jù)結(jié)構(gòu)中,最前面2個字節(jié)是一個固定取值
0xFFD8
,第三個字節(jié)一般也是固定0xFF
。 - 如png格式,它的圖像數(shù)據(jù)結(jié)構(gòu)中,最前面8個字節(jié)就是PNG文件署名域,可以很好的標(biāo)識出當(dāng)前圖像的格式就是PNG。
- 如bmp格式,它的圖像數(shù)據(jù)結(jié)構(gòu)中,最前面14個字節(jié)存儲的是文件頭信息,而最前面2個字節(jié)存儲的就是文件類型:
BM
。 - 如webp格式,需要從最前面移動8個字節(jié)以后,取接下來的4個字節(jié)的信息,代表文件類型:
WEBP
針對不同位圖的的數(shù)據(jù)判斷,可以使用下面表格列出的方式:
其中,偏移量為0,表示取最前面幾個字節(jié)的數(shù)據(jù);webp的偏移量為8,表示從最前面移動8個字節(jié)后,再取4個字節(jié)的標(biāo)識符。
上面的表格,已經(jīng)列出了當(dāng)前瀏覽器支持的位圖圖像,字節(jié)判斷標(biāo)識,通過讀取相應(yīng)的數(shù)據(jù)做對比就得到了真實(shí)的格式。
以上幾種格式中,bmp、gif、webp取到的數(shù)據(jù),都能對應(yīng)各自特有的署名標(biāo)識,前面有提到 BM
和 WEBP
,gif格式的則是 GIF
??梢赃\(yùn)用字符編碼方面的知識,如使用 String.fromCharCode
方法對數(shù)值進(jìn)行轉(zhuǎn)換,具體的前端字符編碼知識見前文前端開發(fā)中需要搞懂的字符編碼
// bmpString.fromCharCode(66) // BString.fromCharCode(77) // M// gifString.fromCharCode(71) // GString.fromCharCode(73) // IString.fromCharCode(70) // F// webpString.fromCharCode(87) // WString.fromCharCode(69) // EString.fromCharCode(66) // BString.fromCharCode(80) // P
gif格式的署名標(biāo)識是和版本號一起處理的,一般最前面6個字節(jié)標(biāo)識: 'G'、'I'、'F'、'8'、'7(9)'、'a'
。第5個字節(jié)可取值7或者9,代表兩個不同的版本,即1987年的版本和1989年的版本。
JS讀取圖片真實(shí)格式
當(dāng)我們了解了前端二進(jìn)制相關(guān)的知識后,就應(yīng)該知道圖片文件也是能通過WebAPI對象,讀取到對應(yīng)的數(shù)據(jù):
const reader = new FileReader()reader.onload = () => { const imgArrayBuffer = reader.result const imgUint8Array = new Uint8Array(imgArrayBuffer)}reader.readAsArrayBuffer(file)
以上代碼,就是通過 FileReader
對象讀取文件的數(shù)據(jù),這里是作為 ArrayBuffer
來讀取的,然后就可以轉(zhuǎn)換成類型數(shù)組進(jìn)行處理了。
讀取到圖片文件的 Uint8Array
類型數(shù)組數(shù)據(jù)后,根據(jù)上文表格中提到的格式字節(jié)數(shù)據(jù)標(biāo)識,我們以jpg、bmp和webp為例:
imgUint8Array[0] === 66 && imgUint8Array[1] === 77 // bmp 格式imgUint8Array[0] === 255 && imgUint8Array[1] === 216 && imgUint8Array[3] === 255 // jpg 格式imgUint8Array[8] === 87 && imgUint8Array[9] === 69 && imgUint8Array[10] === 66 && imgUint8Array[10] === 80 // webp 格式
到此,就可以使用這種方式來讀取到圖片的真實(shí)格式,部分判斷代碼如下:
// 各格式對應(yīng)圖像數(shù)據(jù)的標(biāo)識數(shù)值const IMAGEFORMATS = [ { ext: "png", data: [137, 80, 78, 71, 13, 10, 26, 10] }, { ext: "jpg", data: [255, 216, 255] }, { ext: "gif", data: [71, 73, 70] }, { ext: "ico", data: [0, 0, 1, 0] }, { ext: "bmp", data: [66, 77] }, { ext: "webp", data: [87, 69, 66, 80], offset: 8 }]// 循環(huán)判斷文件是否符合某個格式對應(yīng)的標(biāo)識數(shù)值for (let i = 0; i < IMAGEFORMATS.length; i++) { const { data, offset, ext } = IMAGEFORMATS[i] if (isEqualFormatPrefix(imgUint8Array, data, offset)) { return ext }}
不過以上的方式主要是針對位圖,如果是svg的圖片,則會稍微復(fù)雜一些,需要另行處理。
svg格式的判斷
svg格式圖片是矢量圖,對應(yīng)的數(shù)據(jù)一般使用 xml
標(biāo)記語言進(jìn)行描述,所以我們讀取到圖像數(shù)據(jù)后,需要對應(yīng)的標(biāo)識署名是 <svg
,如果對應(yīng)的圖像數(shù)據(jù)中擁有該標(biāo)識,則大致可以判定為svg格式的圖片。
<svg
標(biāo)識有4個符號和字母,對應(yīng)的數(shù)值:60, 115, 118, 103
,接下來我就需要判斷圖像文件是否有同樣的數(shù)據(jù)了。
imgUint8Array[0] === 60 && imgUint8Array[1] === 115 && imgUint8Array[3] === 118 && imgUint8Array[3] === 103 // svg 格式
以上代碼就是簡單的判斷svg格式了。
但是,我們一般的svg圖片,圖像數(shù)據(jù)最開始是包含有xml標(biāo)記語言的 <?xm
標(biāo)簽,這個時候我們根據(jù)格式再判斷:
if (isEqualFormatPrefix(fileUint8Array, [60, 63, 120, 109], offset)) { // 判斷是否以 <?xm 開頭 if (isHasSignCodes(fileUint8Array, [60, 115, 118, 103])) { // 判斷是否包含 <svg 標(biāo)簽 return"svg" }}
注意:以上針對svg格式矢量圖的這種判斷方式,是以 xml
標(biāo)記語言的標(biāo)簽符號進(jìn)行判斷的,只能處理通過更改后綴名的方式偽造的圖片文件。當(dāng)我們偽造一個假的文件,包含有 <svg
標(biāo)簽標(biāo)識時,則可以逃避這種判斷。
總結(jié)
瀏覽器支持的圖片格式中,除了svg以外,其他幾種位圖格式,都可以較好的通過讀取圖像二進(jìn)制數(shù)據(jù)的方式判斷出圖片文件的真實(shí)格式,能夠防止文件偽造繞開判斷,造成不必要的異常等問題。
到此這篇關(guān)于JavaScript實(shí)現(xiàn)獲取圖片文件真實(shí)格式的示例代碼的文章就介紹到這了,更多相關(guān)JavaScript獲取圖片文件真實(shí)格式內(nèi)容請搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!
