fs.createWriteStream不会立即创建文件?

Jan*_*Kim 34 node.js

从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);
        })
    });
}
Run Code Online (Sandbox Code Playgroud)

但是,由于我download()异步调用了几十次,它很少报告fs.renameSync抱怨它无法找到文件的错误tempFile.path.

Error: ENOENT, no such file or directory 'xxx'
Run Code Online (Sandbox Code Playgroud)

我使用相同的网址列表来测试它,它失败了大约30%的时间.逐个下载时,网址列表相同.

测试了一些,我发现了以下代码

fs.createWriteStream('anypath');
console.log(fs.exist('anypath'));
console.log(fs.exist('anypath'));
console.log(fs.exist('anypath'));
Run Code Online (Sandbox Code Playgroud)

并不总是打印true,但有时打印第一个答案false.

我怀疑太多异步fs.createWriteStream调用无法保证文件创建.这是真的?有没有什么方法可以保证文件的创建?

Joh*_*yHK 63

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);
            });
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

为了您的测试:

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'));
});
Run Code Online (Sandbox Code Playgroud)

  • [fs.js source](https://github.com/joyent/node/blob/master/lib/fs.js#L1690)表示在调用之前无需等待`'open'`事件写入流上的`write`,因为它是在内部处理的. (7认同)

Dan*_*mov 14

接受的答案没有为我下载一些最后的字节.
这是一个正常工作的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
};
Run Code Online (Sandbox Code Playgroud)

注意我正在收听finish文件流而不是end响应.