ФС.createWriteStream не создает файл сразу?



Я сделал простую загрузку из http функции, как показано ниже (обработка ошибок опущена для упрощения):



function download(url, tempFilepath, filepath, callback) {
var tempFile = fs.createWriteStream(tempFilepath);
http.request(url, function(res) {
res.on('data', function(chunk) {
tempFile.write(chunk);
}).on('end', function() {
tempFile.end();
fs.renameSync(tempFile.path, filepath);
return callback(filepath);
})
});
}


Однако, поскольку я вызываю download() десятки раз асинхронно, он редко сообщает об ошибке на fs.renameSync, жалуясь, что не может найти файл в tempFile.path.



Error: ENOENT, no such file or directory 'xxx'


Я использовал тот же список URL-адресов, чтобы проверить его, и он не удался примерно в 30% случаев. Один и тот же список URL-адресов работал при загрузке один за другим.



Протестировав еще немного, я обнаружил, что следующее код



fs.createWriteStream('anypath');
console.log(fs.exist('anypath'));
console.log(fs.exist('anypath'));
console.log(fs.exist('anypath'));


Не всегда печатает true, но иногда первый ответ печатает false.



Я подозреваю, что слишком много асинхронных fs.createWriteStream вызовов не могут гарантировать создание файла. Это правда? Существуют ли какие-либо методы, гарантирующие создание файлов?

517   3  

3 ответов:

Вы не должны вызывать write в своем потоке записи tempFile, пока не получите событие 'open' из потока. Файл не будет существовать, пока вы не увидите это событие.

Для вашей функции:

function download(url, tempFilepath, filepath, callback) {
    var tempFile = fs.createWriteStream(tempFilepath);
    tempFile.on('open', function(fd) {
        http.request(url, function(res) {
            res.on('data', function(chunk) {
                tempFile.write(chunk);
            }).on('end', function() {
                tempFile.end();
                fs.renameSync(tempFile.path, filepath);
                return callback(filepath);
            });
        });
    });
}

Для вашего теста:

var ws = fs.createWriteStream('anypath');
ws.on('open', function(fd) {
    console.log(fs.existsSync('anypath'));
    console.log(fs.existsSync('anypath'));
    console.log(fs.existsSync('anypath'));
});

Принятый ответ не загрузил некоторые из последних байтов для меня.
Вот версия Q, которая работает правильно (но без временного файла).

'use strict';

var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    Q = require('q');

function download(url, filepath) {
  var fileStream = fs.createWriteStream(filepath),
      deferred = Q.defer();

  fileStream.on('open', function () {
    http.get(url, function (res) {
      res.on('error', function (err) {
        deferred.reject(err);
      });

      res.pipe(fileStream);
    });
  }).on('error', function (err) {
    deferred.reject(err);
  }).on('finish', function () {
    deferred.resolve(filepath);
  });

  return deferred.promise;
}

module.exports = {
  'download': download
};
Обратите внимание, что я слушаю finish в файловом потоке вместо end в ответе.

Вот что я использую, чтобы это сделать:

function download(url, dest) {
    return new Promise((resolve, reject) => {
        http.get(url, (res) => {
            if (res.statusCode !== 200) {
                var err = new Error('File couldn\'t be retrieved');
                err.status = res.statusCode;
                return reject(err);
            }
            var chunks = [];
            res.setEncoding('binary');
            res.on('data', (chunk) => {
                chunks += chunk;
            }).on('end', () => {
                var stream = fs.createWriteStream(dest);
                stream.write(chunks, 'binary');
                stream.on('finish', () => {
                    resolve('File Saved !');
                });
                res.pipe(stream);
            })
        }).on('error', (e) => {
            console.log("Error: " + e);
            reject(e.message);
        });
    })
};

Comments

    Ничего не найдено.