Search code examples
c#computer-scienceblazorwebassembly

Save large file created with Cs in Blazor


I am working on a weekend project that aggregates downloads from other sites in a zip file with Blazor (webassmbly).

The zip file is created in c# code and need to be saved on the local computer. I am using a modified version of this answer because the file size can easily extend over 400 mb.

My Version partitions the js call to not imminently overflow the webassembly VM but after a certain filesize it happens and i get the folloring exception:

WASM: Try saving 213776418bytes --my message
blazor.webassembly.js:1 WASM: GC_MAJOR_SWEEP: major size: 1808K in use: 94964K
blazor.webassembly.js:1 WASM: GC_MAJOR: (LOS overflow) time 71.98ms, stw 72.00ms los size: 486832K in use: 481083K
WASM: Error: Garbage collector could not allocate 16384 bytes of memory for major heap section.
Uncaught (in promise) ExitStatus {name: "ExitStatus", message: "Program terminated with exit(1)", status: 1}

Is there currently any other way to save a file from WebAssembly other then transferring the file content to js and then save them with Blob?


Solution

  • I found a solution. With the latest update to .Net5 it is now possible to use the IJSUnmarshalledRuntime. This makes everything a lot easier. Beware there seems to be still limits or other restrictions in place I was forced to partition my files to around 200mb but the solution is very fast and does seem to work "fine" enough. Warning: Works only in Webassembly Blazor!

    First write use a FileUtil:

    public static class FileUtil
    {
        public static void SaveAs(this IJSRuntime js, string filename, MemoryStream data)
        {
            var rt = js as IJSUnmarshalledRuntime;
            var dataArray = data.ToArray();
            rt.InvokeUnmarshalled<string, byte[], object>(
                "saveAsFile",
                filename,
                dataArray);
        }
    }
    

    and then use this js function:

    function saveAsFile(filenamePointer, contentPointer) {
        var parts = Blazor.platform.toUint8Array(contentPointer);
        var filename = BINDING.conv_string(filenamePointer);
    
        var blob = new Blob([parts], { type: "application/octet-stream" });
        if (navigator.msSaveBlob) {
            //Download document in Edge browser
            navigator.msSaveBlob(blob, filename);
        }
        else {
            var link = document.createElement('a');
            link.download = filename;
            link.href = URL.createObjectURL(blob);
            document.body.appendChild(link); // Needed for Firefox
            link.click();
            document.body.removeChild(link);
        }
    }