如何嚴格判斷文件上傳類型?再不會你就out啦!

語言: CN / TW / HK
ead>

theme: condensed-night-purple highlight: atom-one-dark


文件上傳是工作中常見的業務需求,很多情況下,我們需要限制文件的上傳類型,比如只能上傳圖片。通常我們是通過input元素的accept屬性來限制文件的類型:

js <input id="file" type="file" accept="image/*" /> 或者通過截取文件名後綴的方式來判斷:

js const ext = file.name.substring(file.name.lastIndexOf('.') + 1); 這樣做看似沒有毛病,但如果把其他文件的後綴名改為圖片格式,就可以成功突破這個限制。以上兩種方式都不嚴謹,存在一定的安全隱患。那麼應該如何解決這個問題呢?

一、查看文件的頭信息

所有文件在計算機中都是以二進制形式進行存儲的,但二進制數據是不方便做判斷的,我們可以利用 vscode 插件hexdump for VSCode以十六進制的形式查看二進制文件。安裝完成後,點擊右上角的小圖標,即可查看文件的十六進制信息:

01.jpg

那麼,我們分別查看一下jpg png gif的十六進制頭信息:

image.png image.png image.png

多打開幾個文件試試,你會發現同一種類型的文件,他們的頭信息是完全相同的。接下來,我們就可以根據頭信息來判斷文件類型了。

二、根據頭信息判斷文件類型

1. 將文件轉為十六進制字符串

在獲取文件對象後,我們可以通過FileReader API來讀取文件的內容,然後將結果轉為Unicode編碼,再轉為十六進制,以下是我封裝的將文件轉為十六進制字符串的方法: js async blobToString(blob) { return new Promise(resolve => { const reader = new FileReader() reader.onload = function() { const res = reader.result .split("") // 將讀取結果分割為數組 .map(v => v.charCodeAt()) // 轉為 Unicode 編碼 .map(v => v.toString(16).toUpperCase()) // 轉為十六進制,再轉大寫 .map(v => v.padStart(2, "0")) // 個位數補0 .join(" "); // 轉為字符串 resolve(res) } reader.readAsBinaryString(blob) // 將文件讀取為二進制字符串 }) }

2. 判斷文件類型

其實沒有必要將整個文件轉為十六進制,我們只需要截取文件的前幾個字節,然後將截取後的文件轉為十六進制,再進行比對就可以了:

js // 判斷是否為 jpg 格式 async function isJpg(file) { const res = await blobToString(file.slice(0, 3)) return res === 'FF D8 FF' } // 判斷是否為 png 格式 async function isPng(file) { const res = await blobToString(file.slice(0, 4)) return res === '89 50 4E 47' } // 判斷是否為 gif 格式 async function isGif(file) { const res = await blobToString(file.slice(0, 4)) return res === '47 49 46 38' }

3. 完整代碼

```html

Document

```

三、總結

通過文件頭信息,我們除了可以判斷文件的類型,還可以讀取文件相關的元信息,比如圖片的尺寸、位深度、色彩類型和壓縮算法等,只是這些信息所在的位置不一樣。

按照以上方式,大家同樣可以判斷其他格式的文件,常用文件的文件頭。如果你還嫌麻煩,可以使用現成的第三庫來實現這個功能,比如 file-type 這個庫,有興趣的同學可以試一試。

看完記得點個贊呦!萬事開頭難,聽説喜歡點讚的你在 2022 年將迎來一個開門紅!😉

往期精彩

10個常見的前端手寫功能,你全都會嗎?

Element-UI 奇淫技巧第二彈!提升開發效率,延長摸魚時間~

聽説你還不會虛擬列表?原諒我來晚了

一款強大到沒朋友的圖片編輯插件,愛了愛了!

如何實現拖拽上傳、上傳進度條,以及取消上傳?