雜記 › 技術筆記 › 1 GB 文件由 1024 塊拼成:任意大小文件生成的分塊節奏、取消邊界與三種填充位元組

1 GB 文件由 1024 塊拼成:任意大小文件生成的分塊節奏、取消邊界與三種填充位元組

1 GB 由 1024 塊 1048576 位元組拼成,取消只設一個旗標、停在塊邊界:進度區殘留 44% 與 454.00 MB,三種填充模式的位元組截尾與界面顯示口徑各有出入。

按了取消,進度區停在 44%

同事要一個 1 GB 的本地讀寫用例,直接在瀏覽器裡造。填 1 GB、點生成,進度到 44% 時我怕頁面撐不住,按了取消。按鈕當場恢復可點,進度區卻仍掛在原處:44%、454.00 MB、耗時 0.7s 一個字沒清。為什麼停在 454 這種不上不下的數上?答案在塊邊界。

文件是一塊塊攢出來、最後一次性打包的

生成只有一個 while 循環:源碼寫死 var chunkSize = 1024 * 1024,每輪產出一個 1048576 位元組的 Uint8Array 推進本地陣列,末輪按 Math.min(chunkSize, remaining) 截短;陣列攢齊才 new Blob,標籤到 正在打包下载... 才改寫。塊數就是目標位元組除以 1048576:

目標塊數末塊位元組
3 B13
1.5 MB2524288
10 MB101048576
500 MB5001048576
1 GB1024整除,無短塊

1073741824 / 1048576 = 1024,1 GB 恰好由 1024 個等長塊拼成,沒有截短的那一輪。瀏覽器整跑一次 1449ms,面板定稿 1.45s,Blob 收到的 parts 數正是 1024;同機 Node 版逐塊落盤 1494ms,fs.statSync 報 1073741824 位元組。模式也影響快慢:在任意大小文件生成面板裡 100 MB 隨機數據 186ms、全零 133ms,那 53ms 正是隨機分支逐 65536 位元組灌滿的開銷。

取消只有一個賦值,停止落在塊邊界

cancelGenerate 函式體只有 faCancelled = true 一行。檢查點在循環裡每塊開始之前,命中就彈「已取消生成」後 break——取消不是踩煞車,而是"下一塊不再生成",最長延遲是一塊生成時間加一次 15ms 讓出。1 GB 樣本裡 721ms 按下取消,停在第 454 塊、44%,折合約 1.59ms/塊;同次實驗裡 Blob 構造、createObjectURL、a.click 計數全是 0,取消路徑根本沒進打包段,"已生成的 blob 去了哪"沒有對象——那 454 塊掛在局部陣列上,返回後出作用域。

1 GB 的塊節拍與取消落點 44% 721ms 取消,停在第 454 塊 每塊 1048576 位元組 共 1024 塊,本輪只跑滿 454 約 1.59ms/塊 Blob 構造 0 次

圖 1:1 GB 取消樣本的塊邊界與界面殘留

復位只覆蓋兩個按鈕

resetUI 只做兩件事:生成按鈕解禁、取消按鈕藏起來。進度區不在清單上——44%、454.00 MB、耗時 0.7s 原地留著,結果面板仍 display:none。faCancelled 也不自清,靠下一輪 generate 首行重置。補跑一次 10 MB 驗證污染:toast 只有「文件已生成並開始下載」,面板正常出「文件名:testfile.bin大小:10.00 MB (10,485,760 位元組)耗時:0.02s模式:隨機數據」。殘留的是顯示,不是狀態。

每塊寫 3 個文字,進度條只動 45 次

454 塊期間,百分比、已生成、耗時三個元素各寫 454 次文字,進度條的 style 屬性卻只變 45 次——百分比取整後連續約 10 塊同值,重複賦值不算變更。10 MB 那輪變更 11 次,是 10 塊循環加收尾一次 100%。耗時也有兩套精度:進度區取 1 位小數(0.7s),定稿取 2 位(0.02s、1.45s)。

三種填充模式往位元組裡鋪了什麼

全零分支直接 return 清零的塊,首尾 16 位元組全是 00。隨機分支按 Math.min(65536, size - off) 分 16 次灌滿一整塊,同塊內 0x00 出現 4078 次,與每位元組 256 種取值算出的 4096 次均勻期望對得上。自定義是 TextEncoder 編碼後循環覆寫:「你好abc」UTF-8 是 9 位元組 e4 bd a0 e5 a5 bd 61 62 63,鋪進 20 位元組塊得兩個整循環再接 e4 bd;「AB」灌 5 位元組塊得 41 42 41 42 41,即 ABABA。空文字會退化成全零,卻被第 4 道守衛「請輸入自定義填充文字」攔在界面外。

三種模式的產物位元組 全零 首尾 16 位元組 00 00 00 00 00 00 隨機 65536 灌 16 次 00 計 4078 次,期望 4096 自定義 9 位元組鋪 20 位元組塊 e4 bd a0 e5 a5 bd 61 62 63 e4 bd 截尾 AB 鋪 5 位元組塊 41 42 41 42 41 半循環被切

圖 2:三種填充模式的位元組實測與循環截尾

顯示值與真實位元組差在兩處

一處在首屏:fa-size-val 的靜態初值寫成 1.00 MB (1048576 字节),初始化那次 updateSizeInfo() 一跑就被帶千分位的 1.00 MB (1,048,576 字节) 蓋掉。另一處在變色:紅字按真實位元組數比較(bytes > 500 * 1048576 才寫 #ff6666),顯示值卻先四捨五入——524288001 位元組寫著「500.00 MB」,顏色早已是警示紅。進度區只給 humanReadable,不帶精確位元組。

四道守衛與一條硬頂

generate 頂部排著 4 道關卡。0、負數、空串、非數字在 getTotalBytes 裡同走 return 0,界面只有一條「請輸入有效的文件大小」。超 1GB 那條排在確認框之前:填 1024 GB(1073741824000 位元組)只彈「文件過大,請勿超過 1GB」,確認框一次沒彈。恰好 1 GB 因嚴格大於而合法,但要過 500MB 確認;524288000 位元組同樣嚴格大於,靜默生成。600 MB 那次點了否:toast 空、Blob 構造 0 次、按鈕連 disabled 都沒進過。

下載連結只活 1000 毫秒

文件名取 value.trim() || "testfile.bin",只去首尾空格:輸入 " mydata .dat " 得 download 屬性 mydata .dat,noext 不補擴展名。成功鏈路裡 createObjectURL 到 revokeObjectURL 實測間隔 1000ms,file:// 源下前綴形如 blob:null/,1 秒後即被回收。

FAQ

Q:取消後進度區那幾個數字還能信嗎?

是取消那一刻的真實讀數:454.00 MB 與 44% 對應已產出的 454 塊,只是不再更新,resetUI 沒管進度區。

Q:為什麼 1 GB 反而沒有末塊?

1024 整除沒有餘數,Math.min(chunkSize, remaining) 在這一檔永遠取到整塊;換成 1.5 MB 才截出 524288 的短塊。

Q:在 500MB 確認框點了否,算一次失敗嗎?

不算,它連界面狀態都沒改。這次該填多大,同工具另有一篇舊文 按需造一個指定大小的測試文件。

📎 相關文章

文件名提取的三處邊界:結尾斜槓只兜一格、查詢串整塊留下、空行靜默消失 2026-11-04 encodeAll 一開,中文先變成 中:HTML 實體表覆蓋與解碼容錯實測 2026-11-04 HTML 美化與壓縮不是互逆:10 個樣本的往返位元組帳實測 2026-11-04 貼上 100 行分數,第 45 行被悄悄算成 1/4:約分這頁的負號、空行和大數各歸誰管 2026-11-03 把 3. 起步貼進 Markdown 轉 HTML,為什麼有序列表的編號只認第一個數 2026-11-03