Node.js 使用内容处置作为文件名下载文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20132064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Node.js Download File Using Content Disposition as Filename
提问by user3019326
I'm using the Request module to download files, but I'm not quite sure how to pipe the response to an output stream when the filename must come from the 'Content-Disposition' header. So basically, I need to read the response until the header is found, and then pipe the rest to that filename.
我正在使用 Request 模块来下载文件,但是当文件名必须来自“Content-Disposition”标头时,我不太确定如何将响应通过管道传输到输出流。所以基本上,我需要读取响应直到找到标头,然后将其余部分通过管道传输到该文件名。
The examples show something like:
这些示例显示如下内容:
request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'));
request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'));
Where I want to do (pseudocode):
我想做的地方(伪代码):
var req = request('http://example.com/download_latest_version?token=XXX');
var filename = req.response.headers['Content-Disposition'];
req.pipe(fs.createWriteStream(filename));
I could get the filename using the Request callback:
我可以使用请求回调获取文件名:
request(url, function(err, res, body) {
// get res headers here
});
But wouldn't that negate the benefits of using pipe and not loading the downloaded file into memory?
但这不会否定使用管道而不将下载的文件加载到内存中的好处吗?
回答by kberg
I'm reqesting a image from yahoo and it isn't using the content-dispositionheader but I am extracting the dateand content-typeheaders to construct a filename. This seems close enough to what you're trying to do...
我正在从 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]));
});
Ignore my image choice please :)
请忽略我的图像选择:)
回答by Sirko
Question has been around a while, but I today faced the same problem and solved it differently:
问题已经有一段时间了,但我今天遇到了同样的问题并以不同的方式解决了它:
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
});
});
回答by chris
Here's my solution:
这是我的解决方案:
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);
}
});
The other solutions posted here use res.pipe, which can fail if the content is transferred using gzipencoding, because the response stream contains the raw (compressed) HTTP data. To avoid this problem you have to use request.pipeinstead. (See the second example at https://github.com/request/request#examples.)
此处发布的其他解决方案使用res.pipe,如果使用gzip编码传输内容,则可能会失败,因为响应流包含原始(压缩)HTTP 数据。为避免此问题,您必须request.pipe改用。(请参阅https://github.com/request/request#examples 上的第二个示例。)
When using request.pipeI was getting an error: "You cannot pipe after data has been emitted from the response.", because I was doing some async stuff before actually piping (creating a directory to hold the downloaded file). I also had some problems where the file was being written with no content, which might have been due to requestreading the HTTP response and buffering it.
使用时,request.pipe我收到一个错误:“在从响应中发出数据后,您无法进行管道传输。”,因为我在实际管道传输之前做了一些异步操作(创建一个目录来保存下载的文件)。我也遇到了一些问题,文件写入时没有内容,这可能是由于request读取 HTTP 响应并对其进行缓冲。
So I ended up creating an intermediate buffering stream with through2, so that I could pipe the request to it before the response handler fires, then later piping from the buffering stream into the file stream once the filename is known.
所以我最终创建了一个中间缓冲流through2,这样我就可以在响应处理程序触发之前将请求通过管道传输给它,然后在知道文件名后从缓冲流传输到文件流。
Finally, I'm parsing the content disposition header whether the filename is encoded in plain form or in UTF-8 form using the filename*=''file.txtsyntax.
最后,我正在解析内容处置标头,无论文件名是使用filename*=''file.txt语法以纯格式编码还是以 UTF-8 格式编码。
I hope this helps someone else who experiences the same issues that I had.
我希望这可以帮助遇到与我相同问题的其他人。

