I am looking for a working way to to use the GM methods in CollectionFS transformWrite function depending o the image size. There is a size method implemented in GM but this works async and so it seems to be not possible to use.
I tried the following:
gm(readStream, fileObj.name()).size(function(err, dimensions){
if (err) {
console.log('err with getting size:');
console.log(err);
}
console.log('Result of media_size:');
console.log(dimensions);
// here do smth depends on the dimensions ...
gm(readStream, fileObj.name()).resize('1200', '630').stream().pipe(writeStream);
});
When i use the above snippet in the CollectionFS function I get this error:
Error: gm().stream() or gm().write() with a non-readable stream.
This seems to be a problem that I use a async function - when removing the async function the upload works perfectly but then I have no access to the dimensions of the uploaded image.
Is there a solution to get the dimensions of the image in a sync way when having just access to fileObj
, readStream
& writeStream
?
Edit:
Thanks Jasper for the hint with the wrapAsync. I tested it and have this code in use:
var imgsize;
var img = gm(readStream, fileObj.name());
imgsize = Meteor.wrapAsync(img.size, img);
console.log('call wrapAsync:');
var result;
try {
result = imgsize();
} catch (e) {
console.log('Error:');
console.log(e)
}
console.log('((after imgsize()))');
When take a look at the console.logs the script stops after "call wrapAsync" - also there is no error returning so its very difficult to tell whats the problem. I also tried this with the NPM package "imagesize" with Meteor.wrapAsync(imagesize);
and then imgsize(readStream)
which causes the same: No console log after "call wrapAsync:".
The core of the problem is not the asynchronous behavior of gm().size()
, but the fact that you use the readStream
twice. First you use it to get the size of the image, which empties readStream
. Then you try to use it again to resize but because it has ended, you get an error telling you the stream is non-readable.
I found the solution at the bottom of the gm package's streams documenation:
GOTCHA: when working with input streams and any 'identify' operation (size, format, etc), you must pass "{bufferStream: true}" if you also need to convert (write() or stream()) the image afterwards NOTE: this buffers the readStream in memory!
Based on that and the small example below, we can change your code to:
gm(readStream, fileObj.name()).size({ bufferStream: true }, function(err, dimensions){
if (err) {
console.log('err with getting size:');
console.log(err);
}
console.log('Result of media_size:');
console.log(dimensions);
// here do smth depends on the dimensions ...
this.resize('1200', '630').stream().pipe(writeStream);
});
Inside of the callback, this
refers to the image you're working on and you can use it to continue your chain.
I tested this in a small sample meteor application, and it works!