等尺寸重導:來源 12342 位元組,拿回來 281 位元組
「位圖不壓縮、所以體積大」這句話我早想驗一下,驗法很笨:拿一張 64x64 的 BMP(12342 位元組),寬和高都填回 64,等尺寸重導一次。結果下載回來只有 281 位元組,開頭 12 個位元組是 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d——PNG 的魔數接 IHDR,檔名卻寫著 bmp_64_resized.bmp。重讀那個 blob: 位址,type=image/png、size=281,與 resultBlob 逐位元組相同,所以寫進磁碟的位元組本身就是 PNG。
檔名和位元組由兩處程式碼分別產生
模板 image-resize.html 第 227 行讓 mimeType 繼承輸入的 file.type,第 228 行 if (mimeType === 'image/bmp') mimeType = 'image/png';,註解寫著 Canvas 不支援 BMP 輸出。名字歸另一處:第 246 行 originalFile.name.replace(/\.([^.]+)$/, '_resized.$1') 只把原名的副檔名搬進 _resized.$1,沒人去看容器換了沒有。於是調整 BMP 尺寸這條路上兩處各寫各的:跑完的 22 例導出一例 BM 標頭都沒有,21 例 PNG、1 例 WebP,以 BMP 身分進來的 16 例下載檔名全以 .bmp 結尾。
邊界要說清:預探測裡請求 image/bmp 本來就會拿到 image/png,兩種產物同為 1062 位元組,刪掉第 228 行也一樣;但本篇的主證據是模板自己產物的前 12 位元組與下載檔名。
解碼很寬,閘門只看副檔名
13 份結構完整的 BMP 樣張都解出與標頭欄位一致的寬高:1/4/8/16/24/32 bpp、BI_RLE8 的 5436、BI_RLE4 的 2428 位元組、負 biHeight 的 top-down、行寬非 4 對齊的 63x64;被拒的只有 biSize=12 那份 3096 位元組,另兩例是 16 位元組截斷與 0 位元組。24bpp 那份正好是 14+40+192*64=12342 位元組,「不壓縮」在 BI_RGB 這條上算得通。
閘門看的是副檔名給出的 file.type:同一份 BMP 改名 .png、.webp 都進得來並照樣解出 BMP 像素,改名 .dat 與 .txt 則被彈出「不支援的圖片格式」;JPEG 位元組掛上 .bmp 檔名(9712 位元組)會按 BMP 身分轉成 116541 位元組的 PNG,是來源的 12 倍。只有副檔名不是 .bmp 時三者才對得上——改名 .webp 那份真去請求 image/webp、產物 570 位元組、下載檔名也帶著 .webp。站內那篇講副檔名與真實格式為什麼會對不上說的是使用者改名的通用情境,本篇這條是第 228 行與第 246 行自己造出來的不一致。
變小是換容器,不是縮放
等尺寸重導的 12 例產物 IHDR 一律是 bitDepth=8 colorType=6,調色盤與 565 位域統統展開成 RGBA8 再壓。我在那份 32bpp 樣張的左上 16x16 顯式寫了 alpha=0,縮放後畫布 alpha min=255 max=255,透明在解碼階段就掉了;對照一張 1x1 全透明 PNG,放大後 alpha min=0 max=0 活著出來。
| 結構 | 來源位元組 | 等尺寸產物位元組 |
|---|---|---|
| 24bpp BI_RGB,64x64 | 12342 | 281 |
| 8bpp 調色盤,64x64 | 5174 | 577 |
| 16bpp BI_BITFIELDS(565) | 8258 | 1442 |
| BI_RLE4,64x64 | 2428 | 1953 |
同一張圖換個位元深度進來,出去的位元組數幾乎只跟著像素數走:16438 與 12342 兩份都變成 529 位元組。
圖 1:產物位元組佔來源位元組的比例
第 224 行的 drawImage 用模板從未設置的預設檔 imageSmoothingQuality=low,目標尺寸不是 1:1 就要過一次重採樣;糊到什麼程度站內另有一篇講截圖縮小為什麼會糊,本篇不接。
取整兩套打架,品質滑桿對 BMP 無效
寬高框走第 211 與 212 行的 parseInt,是截斷:填 1.5 得 1、2.9 得 2、1e3 得 1 而不是 1000、0.9 得 0 並彈出「請輸入有效的寬高尺寸」。等比連動與百分比走 Math.round、0.5 向上,於是 120x80 那張(aspectRatio=1.5)寬填 100 得高 67,比例被取整成 1.4925;2000x100 那張寬填 9 算出高 0,被第 213 行攔下。鎖定 ON 只做等比連動,改高度框不會反推寬度。
品質那一列在 BMP 輸入時是 display=none,第 229 行卻照樣把值傳進 toBlob:0.1、0.5、1 三檔產物都是 281 位元組,同一根滑桿對 JPEG 來源給出 2038 到 54687 位元組的不同結果。上限只有兩道:檔案側判斷位元組數,20971521 才彈出「圖片大小不能超過 20MB」,等於 20971520 放行,而那兩例樣張是全零 Blob,剛好 20 MiB 的合法 BMP 能不能解開本機沒測過;尺寸側第 216 行只判單邊,10000x10000 也放行。
失敗全靜默,還能帶著上一張出貨
模板只掛第 141 行的 onload,全文找不到 onerror:72 位元組的壞 PNG、0 位元組、16 位元組截斷、biSize=12 的 3096 位元組,四例全是 toast=[],既不報錯也不清空上一張。第 137 行又趕在解碼之前寫了 originalFile:先縮放出 134 位元組的產物,再上傳那個壞檔案,點下載會得到成功語氣的「下載已開始」+新圖的檔名+舊圖的 134 位元組。
批次也沒有:第 120 行與第 124 行只取 files 的第一個,選擇框上沒有 multiple;resetAll() 之後寬高框與品質的殘留值實測還在。站內文案 12 條主張裡 [衝突] 4 條、[部分衝突] 3 條,能站住的只有一句:BMP 進、PNG 出,檔名仍帶著 .bmp。
常見問題
Q:下載回來的那個 .bmp 到底是什麼?
是 PNG。前 12 位元組是 PNG 魔數接 IHDR,blob.type=image/png,容器標頭還是 8 位 RGBA;名字裡的 .bmp 只是第 246 行搬過來的副檔名,沒有任何一行程式碼去改它。
Q:能不能指定輸出的容器?
不能。全文沒有格式選擇器,MIME 只能由第 227 行繼承輸入、再由第 228 行改寫,等尺寸重導也一樣被重編碼成 281 位元組的 PNG;真想要一個位圖容器就得換一條別的路徑。
Q:檔案一定會變小嗎?
12 例都變小,幅度卻差很大:24bpp 只剩 2.28%,本來緊湊的 RLE4 還有 80.4%;反方向是 JPEG 掛 .bmp 檔名漲到 116541 位元組,1x1 全透明 PNG 等尺寸重導從 70 變 88 位元組。