Reputation: 131
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
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