Node.js Загрузить файл с использованием Content Disposition в качестве имени файла
Я использую модуль 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:
request(url, function(err, res, body) {
// get res headers here
});
Но разве это не отрицает преимущества использования канала, а не загружает загруженный файл в память?
Ответы
Ответ 1
Я запрашиваю изображение из 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]));
});
Игнорируйте мой выбор изображения, пожалуйста:)
Ответ 2
Вопрос был около того, но сегодня я столкнулся с той же проблемой и решил ее по-другому:
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
});
});
Ответ 3
Здесь мое решение:
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
, чтобы я мог запросить его до начала обработчика ответа, а затем позже из буферизующего потока в поток файлов, как только имя файла будет известно.
Наконец, я разбираю заголовок содержимого, независимо от того, закодировано ли имя файла в простой форме или в форме UTF-8 с использованием синтаксиса filename*=''file.txt
.
Надеюсь, это поможет кому-то другому, кто испытывает те же проблемы, что и я.