An unexpected empty script to write to an FS collection using graphicsmagick

I am using CollectionFS for image management. In addition, I use graphicsmagick gm() for image management.

Now I want to crop the already saved image. Therefore, when the click event is called, the server method that executes crop () is called. But after that in the collection I find an empty image with size=0 updated on the correct date.

I do not see what I am doing wrong.

shared.js

 Images = new FS.Collection("images", { stores: [ new FS.Store.FileSystem("thumbnail", { transformWrite: function(fileObj, readStream, writeStream) { gm(readStream, fileObj.name()).autoOrient().resize('96', '96' + '^').gravity('Center').extent('96', '96').stream().pipe(writeStream); } }), new FS.Store.FileSystem("public"), ] }); 

server.js

 Meteor.methods({ 'crop': function (fileId, selection) { var file = Images.findOne({ _id: fileId }), read = file.createReadStream('public'), write = file.createWriteStream('public'); gm(read) .crop(selection.width, selection.height, selection.left, selection.top) .stream() .pipe(write); } }); 

client.js

 Template.editor.events({ 'click #crop': function () { var fileId = '123456789', selection = { height: 100, width: 100, top: 10, left: 10 }; Meteor.call('crop', fileId, selection); } }); 

Update

As Christian recommended, I use a tmp file for writeStream because writeStream cannot be the same as readStream, which caused an empty result.

But after writing to the tmp file, its contents must be copied back to the shared storage. How to do it?

 Meteor.methods({ 'crop': function (fileId, selection) { var fs = Meteor.npmRequire('fs'), file = Images.findOne({ _id: fileId }), read = file.createReadStream('public'), filename = '/tmp/gm_' + Date.now(), tmp = fs.createWriteStream(filename); gm(read) .crop(selection.width, selection.height, selection.left, selection.top) .stream() .pipe(tmp); // After writing to tmp -> copy back to stream and delete tmp-file } }); 

Update 2 I tried this:

 // Add temp store new FS.Store.FileSystem("temp") // Method Meteor.methods({ 'crop': function (fileId, selection) { var file = Images.findOne({ _id: fileId }), read = file.createReadStream('public'), temp = file.createWriteStream('temp'); gm(read) .crop(selection.width, selection.height, selection.left, selection.top) .stream() .pipe(tmp) .on('end', function () { var tmpread = file.createReadStream('temp'), write = file.createWriteStream('public'); gm(tmpread).stream().pipe(write); }); } }); 
+5
source share
1 answer

You cannot read and write to the same file. This is equivalent to things like

 cat test | grep 1 > test 

on the shell. You can try and see that after this test will be empty.

You need to create an intermediate temporary file in your crop method.


Assuming this is really a problem, then this is one way to do this (not verified):

 var fs = Meteor.npmRequire('fs'); var file = Images.findOne({ _id: fileId }), var read = file.createReadStream('public'), var filename = '/tmp/gm_' + Date.now(); var tmp = fs.createWriteStream(filename); var gmread = gm(read) .crop(selection.width, selection.height, selection.left, selection.top) .stream(); gmread.on('end', function() { // done streaming through GM, copy the result back: var tmpread = fs.createReadStream(filename); var write = file.createWriteStream('public'); tmpread.pipe(write); }); gmread.pipe(tmp); 
+2
source

Source: https://habr.com/ru/post/1236452/


All Articles