fs.createWriteStream does not immediately create file?
我从http函数进行了如下简单下载(为简化起见,省略了错误处理):
1 2 3 4 5 6 7 8 9 10 11 12 | 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); }) }); } |
但是,由于我异步调用
1 | Error: ENOENT, no such file or directory 'xxx' |
我使用了相同的URL列表进行测试,但失败了大约30%。 一次下载一个相同的URL列表。
测试更多,我发现以下代码
1 2 3 4 | fs.createWriteStream('anypath'); console.log(fs.exist('anypath')); console.log(fs.exist('anypath')); console.log(fs.exist('anypath')); |
并不总是打印
我怀疑太多的异步
在您从流中接收到
为了您的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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); }); }); }); } |
为了您的测试:
1 2 3 4 5 6 | 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版本(但没有临时文件)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | '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 }; |
注意我正在听文件流上的
我正在通过nodejs
我能够提出混合解决方案,在其中我可以同时使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /** * Downloads the file. * @param {string} fileId : File id to be downloaded. * @param {string} downloadFileName : File name to be downloaded. * @param {string} downloadLocation : File location where it will be downloaded. * @param {number} version : [Optional] version of the file to be downloaded. * @returns {string}: Downloaded file's absolute path. */ const getFile = async (fileId, downloadFileName, downloadLocation, version = undefined) => { try { const url = version ? `http://localhost:3000/files/${fileId}?version=${version}` : `${config.dms.url}/files/${fileUuid}`; const fileOutputPath = path.join(downloadLocation, fileName); const options = { method: 'GET', url: url, headers: { 'content-type': 'application/json', }, resolveWithFullResponse: true } // Download the file and return the full downloaded file path. const downloadedFilePath = writeTheFileIntoDirectory(options, fileOutputPath); return downloadedFilePath; } catch (error) { console.log(error); } }; |
如您在上述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * Makes REST API request and writes the file to the location provided. * @param {object} options : Request option to make REST API request. * @param {string} fileOutputPath : Downloaded file's absolute path. */ const writeTheFileIntoDirectory = (options, fileOutputPath) => { return new Promise((resolve, reject) => { // Get file downloaded. const stream = fs.createWriteStream(fileOutputPath); return request .get(options.url, options, (err, res, body) => { if (res.statusCode < 200 || res.statusCode >= 400) { const bodyObj = JSON.parse(body); const error = bodyObj.error; error.statusCode = res.statusCode; return reject(error); } }) .on('error', error => reject(error)) .pipe(stream) .on('close', () => resolve(fileOutputPath)); }); } |
nodejs的优点在于它支持不同异步实现的向后兼容性。如果某个方法正在返回promise,则将插入
上面的
这是我用来完成任务的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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); }); }) }; |