Как в nodeJS запустить несколько потоков чтения/записи одновременно?

sanu0074

Новичок
Делаю загрузку файлов на сервер. Хочу использовать потоки.

Я попытался кое-что сделать:

Код:
//FileSystem handler library
var log = require("core/lib/log")(module);
var fs = require('fs');
var async = require('async');

var fsh = function (files) {

    var $this = this;

    this.moveUploadedFiles = function(dest,cb){
        if(!Object.keys(files).length){
            log.info('fsh.moveUploadedFiles-> files object is empty');
            return cb({state:0}); //files not found
        }

        async.waterfall ([
            function (cb) {
                $this.createFolder(dest,function(){
                    cb(null);
                });
            }
        ], function () {
            var infile = [];
            var outfile = [];
            var uploadSize = [];
            for (var i in files) {
                var path = dest + '/' + files[i].originalFilename;
                infile[i] = fs.createReadStream(files[i].path);
                outfile[i] = fs.createWriteStream(path);
                infile[i].on('data',function(data) {
                    if(!uploadSize[i]){
                        uploadSize[i] = 0;
                    }
                    uploadSize[i] += data.length/1024;
                    console.log(files[i].originalFilename + ': '+(files[i].size/1024) + '/'+uploadSize[i]+' Kb');
                    outfile[i].write(data);
                });
                infile[i].on('close', function() {
                    outfile[i].close();
                });
                infile[i].pipe(outfile[i]);
            }
        });

        return cb({state:1});  //upload success
    };

    this.createFolder = function(dir,cb){
        fs.exists(dir, function (exists) {
            if(exists){
                log.info('fsh.createFolder-> Dir exist: %s',dir);
                return cb();
            }else{
                fs.mkdir(dir,777,function(){
                    log.info('fsh.createFolder-> %s',dir);
                    return cb();
                });
            }
        });
    };


};

module.exports = fsh;
но как-то неправильно работает, несколько файлов за раз не пишет, работает только с одним. Я выбрал два разных файла, в консоли вижу следующее:

Код:
Hawaii-Big-Island-TF.jpg: 1323.076171875/64 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/128 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/192 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/256 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/320 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/384 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/448 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/512 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/576 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/640 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/704 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/768 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/832 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/896 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/960 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1024 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1088 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1152 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1216 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1280 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1344 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1408 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1472 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1536 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1600 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1664 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1728 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1792 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1856 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1920 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/1984 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2048 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2112 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2176 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2240 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2304 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2368 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2432 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2496 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2560 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2624 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2688 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2752 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2816 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2880 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/2944 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3008 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3072 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3136 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3200 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3264 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3328 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3392 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3456 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3520 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3584 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3597.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3661.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3725.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3789.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3853.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3917.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/3981.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4045.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4109.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4173.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4237.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4301.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4365.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4429.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4493.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4557.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4621.8701171875 Kb
Hawaii-Big-Island-TF.jpg: 1323.076171875/4664.9462890625 Kb
В чем может быть проблема?
 

hell0w0rd

Продвинутый новичок
В node нет потоков, есть асинхронность. Посмотри, как работает epoll, чтобы понять, как это все работает.
Единственное, что можно сделать - подключить модуль cluster для входной точки, то есть для самого сервера. Хотя это за тебя должен делать process-manager, на пример pm2.
Ну и глупости ты какие-то делаешь в коде. Вот тебе пример правильный:
Код:
const fs = require('fs');

function cp(from, to) {
  return new Promise((resolve, reject) => {
    const read = fs.createReadStream(from);
    const write = fs.createWriteStream(to);
   // тут надо добавить проверку, чтобы resolve/reject 2 раза не был вызван
    read.on('error', reject);
    write.on('error', reject);
    read.on('close', () => {
      write.close();
      resolve();
    });
    read.on('data', () => console.log('read', from));
    read.pipe(write);
  });
}

const files = [
  './test.txt',
  './test2.txt',
  './test3.txt'
];

Promise.all(files.map((from) => {
  const to = from + '.copy';
  return cp(from, to);
})).then(() => {
  console.log('done');
});
Вот у меня какой вывод:
Код:
read ./test.txt
read ./test2.txt
read ./test3.txt
read ./test.txt
read ./test.txt
read ./test2.txt
read ./test3.txt
read ./test.txt
read ./test2.txt
read ./test3.txt
read ./test.txt
read ./test2.txt
read ./test3.txt
read ./test.txt
....
 

WMix

герр M:)ller
Партнер клуба
() => {} что значит сие? новый синтакс лямды?
 

hell0w0rd

Продвинутый новичок
Я бы даже сказал без своего контекста. У arrow-function нет прототипа, как следствие нет this и потому работает this из родительского контекста.
И потому вот так сделать нельзя:
Код:
const SomeClass = () => {};
new SomeClass();
Ну и теоретически они будут работать быстрее, тк VM не нужно копировать прототип, вызывать конструктор. Хотя на практике, по моим тестам, arrow-function медленнее обычной функции (в node 4.1). А должна быть быстрее.
 

sanu0074

Новичок
Я далек от этой темы, но мне интересно, почему в моем кривом примере, если убрать on('data') и on('close'), оставить только pipe() - файлы нормально грузятся, а вот из-за on('data') создаются конфликты. Все же, чтобы понять как это работает, можете подсказать как завести подобный говно-код:
Код:
//создает stream'ы и делает чтение/запись
var streamRW = function(r,w,name,size){
    var infile = fs.createReadStream(r);
    var outfile = fs.createWriteStream(w);
    var  uploadSize = 0;
    infile.on('data',function(data) {
        uploadSize += data.length/1024;
        console.log(name + ': '+uploadSize+'/'+(size/1024) + ' Kb');
        outfile.write(data);
    });
    infile.on('close', function() {
        outfile.close();
    });
    infile.pipe(outfile);
};

//в цикле для каждого файла делаем чтение/запись, files содержит файлы полученные из формы (req.files), dest - целевая папка
var sRW = [];
for (var i in files) {
  var path = dest + '/' + files[i].originalFilename;
  sRW[i] = new streamRW(files[i].path,path,files[i].originalFilename,files[i].size/1024);
}
 

fixxxer

К.О.
Партнер клуба
У arrow-function нет прототипа
Это теоретически. :) Реализовано это сейчас, похоже, несколько иначе.

PHP:
> var a = (v)=>{return v};
undefined
> a.prototype
undefined

// но

> a.__proto__
[Function]
> Object.getPrototypeOf(a)
[Function]
Совсем не то, что при Object.create(null). Не получится сделать new - это да. А, так сказать, "статические методы" делаются запросто.
 
Последнее редактирование:

sanu0074

Новичок
Например так будет работать:
Код:
for (var i in files) {
                var path = dest + '/' + files[i].originalFilename;
                var infile = fs.createReadStream(files[i].path);
                var outfile = fs.createWriteStream(path);
                infile.pipe(outfile);
            }
Но суть в том чтобы выводить прогресс загрузки, именно для этого я хочу вставить on('data')
 

hell0w0rd

Продвинутый новичок
@fixxxer, есть ощущение, что ES2015 в v8 делался тяп-ляп и в продакшн. https://github.com/nkt/benchmark-es2015, чтобы проверить создал недавно проектик, выхлоп babel, против нативных аналогов.
Код:
Benchmarking Arrow functions...

  Plain Function x 28,853,156 ops/sec ±0.84% (94 runs sampled)
  Bound Function x 468,104 ops/sec ±1.91% (88 runs sampled)
  Arrow Function x 24,006,184 ops/sec ±1.30% (92 runs sampled)

Fastest is Plain Function
Benchmarking Classes...

  ES5 classs x 59,997,369 ops/sec ±0.61% (95 runs sampled)
  ES2015 class x 75,914,454 ops/sec ±1.12% (93 runs sampled)

Fastest is ES2015 class
Benchmarking Template Strings...

  Concat x 89,499,919 ops/sec ±0.84% (93 runs sampled)
  Template x 20,801,390 ops/sec ±1.22% (96 runs sampled)

Fastest is Concat
Только классы работают быстрее, и то, из-за проверки внутри конструктора.
Про промисы я вообще молчу, bluebird и еще парочка библиотек сильно быстрее.
 

fixxxer

К.О.
Партнер клуба
Ага, типа того. Ну можно подобрать набор трансформеров-полифиллов в babel под ноду, один хрен без него никак пока.
 
Сверху