我正在使用Request模块下载文件,但是当文件名必须来自'Content-Disposition'标题时,我不太确定如何将响应传递给输出流。所以基本上,我需要读取响应,直到找到标题,然后将其余部分传递给该文件名。
示例显示如下:
request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'));
我想做的事(伪代码):
var req = request('http://example.com/download_latest_version?token=XXX');
var filename = req.response.headers['Content-Disposition'];
req.pipe(fs.createWriteStream(filename));
我可以使用请求回调获取文件名:
request(url, function(err, res, body) {
// get res headers here
});
但这不会否定使用管道而不是将下载的文件加载到内存中的好处吗?
答案 0 :(得分:31)
我正在从yahoo请求图片并且它没有使用content-disposition
标头,但我正在提取date
和content-type
标头以构建文件名。这似乎与你要做的事情相近......
var request = require('request'),
fs = require('fs');
var url2 = 'http://l4.yimg.com/nn/fp/rsz/112113/images/smush/aaroncarter_635x250_1385060042.jpg';
var r = request(url2);
r.on('response', function (res) {
res.pipe(fs.createWriteStream('./' + res.headers.date + '.' + res.headers['content-type'].split('/')[1]));
});
请忽略我的形象选择:)
答案 1 :(得分:13)
问题已经存在了一段时间,但我今天遇到了同样的问题,并以不同的方式解决了问题:
var Request = require( 'request' ),
Fs = require( 'fs' );
// RegExp to extract the filename from Content-Disposition
var regexp = /filename=\"(.*)\"/gi;
// initiate the download
var req = Request.get( 'url.to/somewhere' )
.on( 'response', function( res ){
// extract filename
var filename = regexp.exec( res.headers['content-disposition'] )[1];
// create file write stream
var fws = Fs.createWriteStream( '/some/path/' + filename );
// setup piping
res.pipe( fws );
res.on( 'end', function(){
// go on with processing
});
});
答案 2 :(得分:4)
这是我的解决方案:
var fs = require('fs');
var request = require('request');
var through2 = require('through2');
var req = request(url);
req.on('error', function (e) {
// Handle connection errors
console.log(e);
});
var bufferedResponse = req.pipe(through2(function (chunk, enc, callback) {
this.push(chunk);
callback()
}));
req.on('response', function (res) {
if (res.statusCode === 200) {
try {
var contentDisposition = res.headers['content-disposition'];
var match = contentDisposition && contentDisposition.match(/(filename=|filename\*='')(.*)$/);
var filename = match && match[2] || 'default-filename.out';
var dest = fs.createWriteStream(filename);
dest.on('error', function (e) {
// Handle write errors
console.log(e);
});
dest.on('finish', function () {
// The file has been downloaded
console.log('Downloaded ' + filename);
});
bufferedResponse.pipe(dest);
} catch (e) {
// Handle request errors
console.log(e);
}
}
else {
// Handle HTTP server errors
console.log(res.statusCode);
}
});
此处发布的其他解决方案使用res.pipe
,如果使用gzip
编码传输内容,则可能会失败,因为响应流包含原始(压缩)HTTP数据。要避免此问题,您必须使用request.pipe
。 (参见https://github.com/request/request#examples的第二个例子。)
使用request.pipe
时出现错误:"从响应中发出数据后,您无法进行管道处理。",因为我在实际管道之前做了一些异步处理(创建了一个用于保存下载文件的目录)。我也遇到了一些问题,其中文件没有内容写入,这可能是由于request
读取HTTP响应并缓冲它。
所以我最终创建了一个带有through2
的中间缓冲流,这样我就可以在响应处理程序触发之前将请求传递给它,然后在知道文件名后从缓冲流传输到文件流中
最后,我正在解析内容处置标头,无论文件名是以普通形式编码还是使用filename*=''file.txt
语法以UTF-8格式编码。
我希望这可以帮助遇到同样问题的其他人。