1
var promisePipe = require("promisepipe");
var fs = require("fs");
var crypt = require("crypto");
var // ....


files = ['/mnt/Storage/test.txt', '/mnt/Storage/test2.txt', '/mnt/Storage/test3.txt']

var promises = files.map(function(file_enc) {
  return new Promise(function(resolve, reject) {
    var file_out = file_enc + '.locked';
    promisePipe(
      fs.createReadStream(file_enc),
      crypt.createCipheriv(alg, genhashsub, iv),
      fs.createWriteStream(file_out),
    ).then(function(streams){
      console.log('File written: ' + file_out);
      // Promise.resolve(file_out); // tried but doesnt seem to do anything
    }, function(err) {
      if(err.message.substring(0, 7) === 'EACCES:') {
        console.log('Error (file ' + file_out + '): Insufficient rights on file or folder');
      } else {
        console.log('Error (file ' + file_out + '): ' + err);
      }
      // Promise.reject(new Error(err)); // tried but doesnt seem to do anything

    });
  })
});

Promise.all(promises).then(final_function(argument));

I'm trying to encrypt files contained in an array named files.
For the sake of simplicity I added them manually in this example.

What I want to happen:

  1. Create promises array to call with promises.all on completion
  2. Iterate through the array
    1. Create promise for this IO operation
    2. Read file \
    3. Encrypt file -- all done using streams, due to large files (+3GB)
    4. Write file /
    5. On finish write, resolve promise for this IO operation
  3. Run finishing script once all promises have resolved (or rejected)

What happens:

  1. Encryption of first file starts
  2. .then(final_function(argument)) is called
  3. Encryption of first file ends

The files all get encrypted correctly and they can be decrypted afterwards.
Also, errors are displayed, as well as the write confirmations.

I've searched both Stack as well as Google and I have found some similar questions (with answers). But they don't help because many are outdated. They work, until I rewrite them into promises, and then I'm back to where I started.

I could also place 8 different ways to achieve this job, using npm modules or vanilla code, but all of them are also failing in one way or another.

Alex Lias
  • 49
  • 7
  • What if you try calling `resolve(file_out)` instead of `Promise.resolve(file_out)`? – Jekrb May 21 '18 at 00:45
  • No difference as far as I can see, the final_function(argument) is still called before all files have been written – Alex Lias May 21 '18 at 01:05
  • thanks @Gino Mempin for question rewrite suggestions, approved! – Alex Lias May 21 '18 at 01:21
  • @AlexLias `final_function` is being called before the files are written because you are _calling it_ on that last line of the code. It will execute immediately. `then` expects a function, but you are passing whatever `final_function(argument)` returns. – JLRishe May 21 '18 at 04:22

2 Answers2

0

an analog of short , file-based process wrapped in a promise all . You could do your encrypt in the 'encrypt' which is wrapped in file handler. encrypt() returns a promise.

segments passes your array of files needing work.

  var filHndlr = function(segment){
    var uri = segment.uri;
    var path = '/tmp/' + uri;
    return that.getFile(path)
      .then (datatss => {
        return that.encrypt(uri, datatss); 
      });
  }
... 

        Promise.all(segments.map(filHndlr))
        .then(resp => { ... });
Robert Rowntree
  • 6,230
  • 2
  • 24
  • 43
0

If you already have a promise at your disposal (and promisepipe appears to create a promise), then you generally should not use new Promise(). It looks like your main problem is that you are creating promises that you never resolve.

The other problem is that you are calling final_function in the last line instead of passing a function that will call final_function.

I suggest giving this a try:

var promises = files.map(function(file_enc) {
  var file_out = file_enc + '.locked';

  return promisePipe(
      fs.createReadStream(file_enc),
      crypt.createCipheriv(alg, genhashsub, iv),
      fs.createWriteStream(file_out),
  ).then(function(streams){
     console.log('File written: ' + file_out);

     return file_out;
  }, function(err) {
    if(err.message.substring(0, 7) === 'EACCES:') {
      console.log('Error (file ' + file_out + '): Insufficient rights on file or folder');
    } else {
      console.log('Error (file ' + file_out + '): ' + err);
    }

    throw new Error(err);
  });
});

Promise.all(promises).then(() => final_function(argument));
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Works perfectly, my eternal thanks not just for the working code, but also for the background information! I still have a lot to learn about the concept of what a Promise actually is – Alex Lias May 21 '18 at 04:44
  • I have made one change, removed the 'throw new Error(err);' at the end. This line causes the promise to reject, which triggers the .then, while other files are still encrypting. (not sure if my explanation is correct, feel free to correct me!) – Alex Lias May 21 '18 at 04:48
  • @AlexLias Yes, if one of them were to fail, that would cause the Promise.all to reject after the first failure. You can simply remove the `throw new Error` and have it swallow the errors, or you could use something like [promise-settle](https://www.npmjs.com/package/promise-settle), which resolves when all of the promises have either resolved or rejected. – JLRishe May 21 '18 at 05:13
  • Very clear, thank you for confirming my thoughts here! Also I see the (initial) problem with the .then, working with .pipe() for days has blinded me somewhat :x – Alex Lias May 21 '18 at 05:16