Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js fs.createWriteStream是否不立即创建文件?_Node.js - Fatal编程技术网

Node.js fs.createWriteStream是否不立即创建文件?

Node.js fs.createWriteStream是否不立即创建文件?,node.js,Node.js,我从http函数下载了一个简单的函数,如下所示(简化时省略了错误处理): 函数下载(url、tempFilepath、filepath、回调){ var tempFile=fs.createWriteStream(tempFilepath); 请求(url,函数(res){ res.on('data',函数(块){ tempFile.write(块); }).on('end',function(){ tempFile.end(); 重命名同步(tempFile.path,filepath); 返

我从http函数下载了一个简单的函数,如下所示(简化时省略了错误处理):

函数下载(url、tempFilepath、filepath、回调){
var tempFile=fs.createWriteStream(tempFilepath);
请求(url,函数(res){
res.on('data',函数(块){
tempFile.write(块);
}).on('end',function(){
tempFile.end();
重命名同步(tempFile.path,filepath);
返回回调(filepath);
})
});
}
然而,当我异步调用
download()
数十次时,它很少在
fs.renameSync
上报告错误,抱怨它在
tempFile.path
上找不到文件

Error: ENOENT, no such file or directory 'xxx'
我使用了相同的URL列表来测试它,大约30%的时间失败了。当一个接一个地下载时,相同的URL列表也起作用

通过进一步测试,我发现以下代码

fs.createWriteStream('anypath');
log(fs.exist('anypath');
log(fs.exist('anypath');
log(fs.exist('anypath');
不总是打印
true
,但有时第一个答案打印
false


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

在收到流中的
'open'
事件之前,您不应该在
tempFile
写入流中调用
write
。在您看到该事件之前,该文件将不存在

对于您的功能:

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'));
});

接受的答案没有为我下载最后的一些字节。
这是一个正常工作的版本(但没有临时文件)


注意,我在文件流中收听的是
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);
        });
    })
};

我正在通过nodejs
request promise
request
库上传和下载文件(docx、pdf、文本等)

request promise
的问题在于,他们没有从
request
包中承诺
pipe
方法。因此,我们需要用老办法来做

我能够提出混合解决方案,在这里我能够同时使用
async/await
Promise()
。以下是一个例子:

    /**
     * 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);
        }
    };
正如您在上面的
getFile
方法中所看到的,我们正在使用支持的最新ES
async/await
功能进行异步编程。现在,让我们看看
writeTheFileIntoDirectory
方法

/**
 * 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));
    });
}
/**
*发出RESTAPI请求并将文件写入提供的位置。
*@param{object}options:Request选项以发出REST API请求。
*@param{string}fileOutputPath:下载文件的绝对路径。
*/
常量writeTheFileIntoDirectory=(选项,fileOutputPath)=>{
返回新承诺((解决、拒绝)=>{
//下载文件。
const stream=fs.createWriteStream(fileOutputPath);
退货申请
.get(options.url,options,(err,res,body)=>{
如果(res.statusCode<200 | | res.statusCode>=400){
const bodyObj=JSON.parse(body);
常量错误=bodyObj.error;
error.statusCode=res.statusCode;
返回拒绝(错误);
}
})
.on('error',error=>reject(error))
.水管(溪流)
.on('close',()=>resolve(fileOutputPath));
});
}
nodejs的优点在于它支持不同异步实现的向后兼容性。如果一个方法返回承诺,那么
wait
将被踢入并等待该方法完成


上面的
writeTheFileIntoDirectory
方法将下载文件,并在成功关闭流时返回肯定值,否则将返回错误。

不知何故,这段代码没有下载最后的一些字节。相反,我现在在
tempFile
上听
finish
,而不是手动执行
res.pipe(tempFile)
。指示在对写入流调用
write
之前不需要等待
事件,因为这是内部处理的。
open
确实是内部处理的,但是它是异步的,直到发出
open
事件,文件才会打开。
/**
 * 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));
    });
}