簡體   English   中英

如何在 JavaScript web worker 中異步解壓縮 gzip 文件?

[英]How can I asynchronously decompress a gzip file in a JavaScript web worker?

我有一個在瀏覽器中運行的普通 JS 腳本(Chrome,並且只需要支持 Chrome——如果瀏覽器支持很重要的話)。

我想將一個 15 MB 的 gzip 文件卸載到 web worker 並異步解壓縮該文件,然后將未壓縮的數據返回到主線程,以免在解壓縮過程中凍結主應用程序線程。

在主線程中解壓縮時,我使用的是JSXCompressor library ,它工作正常。 然而,由於這個庫引用了 window object,它不能從工作環境訪問,我不能使用注入工作代碼的同一個庫(運行解壓會在庫的第一行引發異常,提到“window “,說它是未定義的)。

我在一個下午的谷歌搜索中設法挖掘出的其他 JS 庫也是如此,比如 zlib 或更現代的Pako 它們似乎都以某種方式引用 DOM 元素,當在 web worker 上下文中使用時會引發異常。

所以我的問題是 - 有沒有人知道我可以解決這個問題的方法,要么通過黑客向我解釋我似乎出錯的地方,要么通過向我提供指向 function 的 JS 庫的鏈接用例(我只需要解壓,標准gzip)?

編輯:我也對任何可以利用內置瀏覽器功能進行解壓縮的 hack 感興趣,就像對 HTTP 請求所做的那樣。

非常感謝。

我編寫了一個庫fflate來完成這個任務。 它提供了它支持的每種壓縮/解壓縮方法的異步版本,但不是在事件循環中運行,庫將處理委托給一個單獨的線程。 您不需要手動創建工作人員或指定包內部工作人員的路徑,因為它會即時生成它們。

import { gunzip } from 'fflate';
// Let's suppose you got a File object (from, say, an input)
const reader = new FileReader();
reader.onloadend = () => {
  const typedArrayUncompressed = new Uint8Array(reader.result);
  gunzip(typedArrayUncompressed, (err, gzippedResult) => {
    // This is a Uint8Array
    console.log('Compressed output:', gzippedResult);
  });
}
reader.readAsArrayBuffer(fileObject);

實際上,您需要將輸入格式轉換為 Uint8Array,然后將 output 格式轉換為您想要使用的任何格式。 例如, FileReader是最跨平台的文件解決方案, fflate.strToU8fflate.strFromU8用於字符串轉換。

PS 這實際上仍然與我測試的原生CompressionStream解決方案一樣快,但可以在更多瀏覽器中使用。 如果你想要流媒體支持,使用fflate的AsyncGunzip stream class。

有一個新的 web API Compression streams proposal,它已經在 Chrome 中實現了,它就是這樣做的:異步壓縮/解壓縮數據。

它應該同時支持deflategzip算法,並且應該使用本機實現 -> 比任何庫都快。

所以在 Chrome 中你可以簡單地做:

 if( "CompressionStream" in window ) { (async () => { // To be able to pass gzipped data in stacksnippet we host it as a data URI // that we do convert to a Blob. // The original file is an utf-8 text "Hello world" // which is way bigger once compressed, but that's an other story;) const compressed_blob = await fetch("data:application/octet-stream;base64,H4sIAAAAAAAAE/NIzcnJVyjPL8pJAQBSntaLCwAAAA==").then((r) => r.blob()); const decompressor = new DecompressionStream("gzip"); const decompression_stream = compressed_blob.stream().pipeThrough(decompressor); const decompressed_blob = await new Response(decompression_stream).blob(); console.log("decompressed:", await decompressed_blob.text()); })().catch(console.error); } else { console.error("Your browser doesn't support the Compression API"); }

顯然,這在 Web Workers 中也可用,但由於 API 被設計為完全異步的,並且使用了 Streams,理論上瀏覽器應該已經能夠將所有艱苦的工作外包給自己的另一個線程。


現在,這仍然是一個未來的解決方案,今天您可能仍想使用庫。

但是,我們不在這里推薦庫,但我應該指出,我個人每天都在 Web Workers 中使用 pako,沒有問題,而且我不明白為什么壓縮庫需要 DOM,所以我懷疑你做錯了什么™

 (async() => { const worker_script = ` importScripts("https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.11/pako_inflate.min.js"); self.onmessage = async (evt) => { const file = evt.data; const buf = await file.arrayBuffer(); const decompressed = pako.inflate(buf); // zero copy self.postMessage(decompressed, [decompressed.buffer]); }; `; const worker_blob = new Blob([worker_script], { type: "application/javascript" }); const worker_url = URL.createObjectURL(worker_blob); const worker = new Worker(worker_url); const compressed_blob = await fetch("data:application/octet-stream;base64,H4sIAAAAAAAAE/NIzcnJVyjPL8pJAQBSntaLCwAAAA==").then((r) => r.blob()); worker.onmessage = ({ data }) => { console.log("received from worker:", new TextDecoder().decode(data)); }; worker.postMessage(compressed_blob); })().catch(console.error);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM