Piotr Kabaciński
Piotr Kabaciński

Reputation: 131

Exporting file from Wasm memory to JS

I try to create a Wasm module that will return a newly generated ZIP file to JavaScript (just an idea for my own project). Everything seem to be fine (source file seems to be properly allocated in Wasm file system, and processed by zip library), but I'm not sure how to properly pass it to JS. I guess it should be done by returning a zip file pointer and its bytes length from Wasm memory, and using that creating a bufferArray that will be transformed to Blob. Are there any guesses how to do it right?

I'm using emscripten for compilation process.

#include <fstream>
#include <iostream>
#include <emscripten.h>

#include "lib/zip.h"

using namespace std;

EM_JS(void, call_getZip, (filebuf * buf, size_t length), {
    // Call JS with pointer to buffer and buffer's length
    getZip(buf, length);
});

extern "C"
{
    ifstream file;

    void readFile(int *buffer, int length)
    {   
        // getFileName is a function that returnes file name from JS
        string fileName = getFileName(buffer, length);

        // Open file from WASM file system
        ifstream file;
        file.open("/" + fileName);

        filebuf *fbuf = file.rdbuf();
        size_t size = fbuf->pubseekoff(0, file.end, file.in);

        if (!file.is_open())
        {
            cout << "Failed to open file" << endl;
        }
        else
        {
            struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');

            zip_entry_open(zip, fileName.c_str());
            {
                zip_entry_write(zip, fbuf, size);
            }
            zip_entry_close(zip);
            zip_close(zip);

            ifstream zipFile;
            zipFile.open("foo.zip");

            if (zipFile.is_open())
            {
                cout << "Zip file exists!" << endl;

                filebuf *zipFileBuf = zipFile.rdbuf();

                // I assume this value will be equivalent to the length of allocated memory
                size_t zipFileSize = zipFileBuf->pubseekoff(0, file.end, file.in);

                call_getZip(zipFileBuf, size);
            }
            else
            {
                cout << "Problem with zip file?" << endl;
            }

            zipFile.close();
        }

        file.close();
    }
}

JavaScript:

function getZip(pointer, length) {
  // This approach doesn't work
  console.log(pointer, length);
  const buffer = new Uint32Array(Module.HEAP32, pointer, length);

  const fileUrl = URL.createObjectURL(
    new Blob([buffer], { type: "application/zip" })
  );

  const link = document.createElement("a");
  link.href = fileUrl;
  document.body.appendChild(link);
  link.click();
}
// ...

const file = fileInput.files[0];

const { buffer, length, free } = allocateFileName(file.name);

const fr = new FileReader();

fr.onload = function() {
const fileData = new Uint8Array(fr.result);

Module["FS_createDataFile"](
    "/",
    file.name,
    fileData,
    true,
    true,
    true
);

Module.ccall("readFile", ["number", "number"], "void", [
    buffer,
    length
]);

free();
// console.log(Module);
fileInput.value = null;
};

fr.readAsArrayBuffer(file);

Thanks a lot for any advices in advance, I know I could made some silly mistakes here, therefore I'll be thankful for any clues and ideas.

Upvotes: 0

Views: 1339

Answers (1)

Piotr Kabaciński
Piotr Kabaciński

Reputation: 131

Meanwhile I found the answer. Thanks to emscripten's File System API, it's super convenient to get file contents from wasm memory in JS:

function getZip() {
  // "foo.zip" file name is for now hardcoded
  const content = FS.readFile("foo.zip", { encoding: "binary" });

  const fileUrl = URL.createObjectURL(
    new Blob(
      [new Uint8Array(content, content.byteOffset, content.length)],
      { type: " application/zip" }
    )
  );

  const link = document.createElement("a");
  link.href = fileUrl;
  link.innerHTML = "Click!";
  document.body.appendChild(link);
  link.click();
}

If someone's interested, the full working code can be found in this repo.

Upvotes: 2

Related Questions