将文件从Wasm内存导出到JS

时间:2019-11-14 19:07:21

标签: javascript c++ emscripten webassembly

我尝试创建一个Wasm模块,它将一个新生成的ZIP文件返回给JavaScript(这只是我自己的项目的一个主意)。一切似乎都很好(源文件似乎已在Wasm文件系统中正确分配,并由zip library处理),但是我不确定如何正确地将其传递给JS。我猜应该通过从Wasm内存中返回一个zip文件指针及其字节长度,并使用它创建一个将转换为Blob的bufferArray来完成。有任何猜测怎么做对吗?

我正在使用emscripten进行编译。

#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);

非常感谢您的任何建议,我知道我在这里可能犯了一些愚蠢的错误,因此,我将感谢您提供的任何线索和想法。

1 个答案:

答案 0 :(得分:0)

与此同时,我找到了答案。多亏了emscripten's File System API,从JS的wasm内存中get file contents的使用非常方便:

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();
}

如果有人感兴趣,可以在this repo中找到完整的工作代码。