kleisauke / wasm-vips

libvips for the browser and Node.js, compiled to WebAssembly with Emscripten.
https://kleisauke.github.io/wasm-vips/
MIT License
463 stars 25 forks source link

No paths work when reading files (Deno) #59

Open jtoppine opened 7 months ago

jtoppine commented 7 months ago

In Deno, vips.Image.newFromBuffer(buffer) works well! But opening local files directly for example vips.Image.newFromFile('sample.jpg') always complains:

error: vips::Error: unable to load from file sample.jpg
VipsForeignLoad: file "sample.jpg" does not exist

I thought it would be an issue of relative paths, but I tried all kinds of absolute and relative paths (relative to Deno.cwd(), or import.meta.url, or the directory where vips-es6.js is stored), with and without file:// prefix, with no luck.

Furthermore I tried a simple filename without path, and put a sample.jpg in every directory I could think of where it might try to load it. No luck.

Am I missing something, or is this is a bug with wasm-vips on deno?

Deno 1.38.5, wasm-vips version is i guess 0.0.7 (or whatever it is that jsdelivr is giving - I manually downloaded vips-es6.js and all the other JS and WASM file from jsdelivr in order to install and import locally since it seems using jsdelivr the wasm files are loaded again over the net every run? - could we perhaps somehow have these zipped on new releases?)

PS:

I'm running on VPS's with limited RAM, so while ArrayBuffers work, I'm hoping passing file paths directly would negate the need for allocating big in and out buffers, and perhaps allow vips to process data in a more streaming manner (if it does that, or does it need to load the full file data in memory?).

Ideally for Deno, it would be great if we were able to work with ReadableStream in and WritableStream out, but that does not appear possible at the moment. That's why I'm looking into passing file paths instead of ArrayBuffer

kleisauke commented 7 months ago

Sorry, this dropped off my radar. Currently, wasm-vips doesn't support native file system access in environments other than Node.js, mainly because these environments typically reuses the web ES6 module. Emscripten issue https://github.com/emscripten-core/emscripten/issues/12203 might be relevant here.

On the web, the whole filesystem is virtualized (see MEMFS). So, you need to preload the file first, for example on Deno:

import Vips from 'https://cdn.jsdelivr.net/npm/wasm-vips/lib/vips-es6.js';

const vips = await Vips({
  // Optimize startup time by disabling the dynamic modules
  dynamicLibraries: [],
  preRun: async (module) => {
    // Preload banana.webp
    module.FS.createDataFile('/', 'banana.webp', await Deno.readFile('playground/src/images/banana.webp'), true, false);
  }
});

// Load an image from a preloaded file
const im = vips.Image.newFromFile('banana.webp[n=-1]', {
  access: vips.Access.sequential // 'sequential'
});

// Write the result to a GIF file
const outBuffer = im.writeToBuffer('.gif');
await Deno.writeFile('banana.gif', outBuffer);

// Memory management
im.delete();

// Exit the Deno runtime (i.e. a more "commandline" experience)
Deno.exit();
$ deno run --allow-net --allow-read --allow-write test.js

it seems using jsdelivr the wasm files are loaded again over the net every run?

IIUC, Deno reuses a module from its cache without having to fetch it again on subsequent runs, but I'm not sure how Deno manages Wasm files internally.

could we perhaps somehow have these zipped on new releases?

wasm-vips will only be distributed via npmjs.com; there are currently no plans to distribute it through other channels.

allow vips to process data in a more streaming manner

I think the source/target API of libvips would be suitable for this. https://www.libvips.org/2019/11/29/True-streaming-for-libvips.html https://github.com/kleisauke/wasm-vips/blob/65f364dc49053b3bf0c7ef39d0a0af1beb8c7506/test/unit/test_connection.js#L81-L100

it would be great if we were able to work with ReadableStream in and WritableStream out

Indeed, this would be a great addition. It can probably be implemented using a new pair of Image.newFromStream() / image.writeToStream() functions.

jtoppine commented 7 months ago

Thank you, lot of valuable information there. And no worries. I ended up interfacing with native vips cli (for now), and turned out working very well. Still interested in using wasm-vips sometime in the future, though!

I was wondering what types the Source & Target related methods were referring to. Indeed it looks like one could use that kind of lower level interface to make an adapter that would work with almost any type of IO access, including ReadableStream/WritableStream. Feels like a nice project idea for a deno.land/x module, a wrapper of sorts that would feel more "deno native". Might even take up the challenge sometime in the future, no promises though!

On the other hand, having newFromStream/writeToStream methods built into wasm-vips might make sense outside of deno too, since them being standard web apis, could be useful in some purely client side browser apps.