Node.js Download File Using Content Disposition as Filename

前端 未结 3 986
栀梦
栀梦 2021-02-01 17:22

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\

相关标签:
3条回答
  • 2021-02-01 17:50

    I'm reqesting a image from yahoo and it isn't using the content-disposition header but I am extracting the date and content-type headers to construct a filename. This seems close enough to what you're trying to do...

    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 :)

    0 讨论(0)
  • 2021-02-01 18:00

    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 gzip encoding, because the response stream contains the raw (compressed) HTTP data. To avoid this problem you have to use request.pipe instead. (See the second example at https://github.com/request/request#examples.)

    When using request.pipe I 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 request reading the HTTP response and buffering it.

    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.

    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.txt syntax.

    I hope this helps someone else who experiences the same issues that I had.

    0 讨论(0)
  • 2021-02-01 18:06

    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
                        });
                     });
    
    0 讨论(0)
提交回复
热议问题