从 Wasm 内存导出文件到 JS
Exporting file from Wasm memory to JS
我尝试创建一个 Wasm 模块,它将 return 新生成的 ZIP 文件 JavaScript (这只是我自己项目的一个想法)。一切似乎都很好(源文件似乎已在 Wasm 文件系统中正确分配,并由 zip library 处理),但我不确定如何将其正确传递给 JS。我想这应该通过从 Wasm 内存中 returning 一个 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);
非常感谢您提前提出任何建议,我知道我可能会在这里犯一些愚蠢的错误,因此我将感谢您提供任何线索和想法。
同时我找到了答案。感谢 emscripten's File System API, it's super convenient to get file contents 来自 JS 中的 wasm 内存:
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 中找到完整的工作代码。
我尝试创建一个 Wasm 模块,它将 return 新生成的 ZIP 文件 JavaScript (这只是我自己项目的一个想法)。一切似乎都很好(源文件似乎已在 Wasm 文件系统中正确分配,并由 zip library 处理),但我不确定如何将其正确传递给 JS。我想这应该通过从 Wasm 内存中 returning 一个 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);
非常感谢您提前提出任何建议,我知道我可能会在这里犯一些愚蠢的错误,因此我将感谢您提供任何线索和想法。
同时我找到了答案。感谢 emscripten's File System API, it's super convenient to get file contents 来自 JS 中的 wasm 内存:
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 中找到完整的工作代码。