Node.js send file in response
Asked Answered
L

5

145

Expressjs framework has a sendfile() method. How can I do that without using the whole framework?

I am using node-native-zip to create an archive and I want to send that to the user.

Lon answered 6/4, 2012 at 16:6 Comment(0)
P
237

Here's an example program that will send myfile.mp3 by streaming it from disk (that is, it doesn't read the whole file into memory before sending the file). The server listens on port 2000.

[Update] As mentioned by @Aftershock in the comments, util.pump is gone and was replaced with a method on the Stream prototype called pipe; the code below reflects this.

var http = require('http'),
    fileSystem = require('fs'),
    path = require('path');

http.createServer(function(request, response) {
    var filePath = path.join(__dirname, 'myfile.mp3');
    var stat = fileSystem.statSync(filePath);

    response.writeHead(200, {
        'Content-Type': 'audio/mpeg',
        'Content-Length': stat.size
    });

    var readStream = fileSystem.createReadStream(filePath);
    // We replaced all the event handlers with a simple call to readStream.pipe()
    readStream.pipe(response);
})
.listen(2000);

Taken from http://elegantcode.com/2011/04/06/taking-baby-steps-with-node-js-pumping-data-between-streams/

Patent answered 6/4, 2012 at 17:35 Comment(8)
But I'm not streaming a file from the server, I create the archiveLon
By "stream" I mean "send the file data to the connection as it's being read" as opposed to "read the whole file in memory then send all that data to the connection at once" (which is the typical naive approach). I don't mean "stream the data from memory without it going to disk." The post I linked to explains in more detail.Patent
util.pump(readStream, response); is depreciated... use readStream.pipe(response);Volotta
This is not safe. See: https://mcmap.net/q/161062/-node-js-stream-api-leakHemotherapy
@Abdul Not sure I understand your question; wanna shoot me an email?Patent
Tried it but got net::ERR_CONTENT_LENGTH_MISMATCH error.Collbaith
@MichelleTilley: Is it possible to continuously read from a HTTP endpoint like elasticsearch and still write to a downloadable file by modifying your code?Counts
@MichelleTilley, so does this give data of zip in chunk formart?something like this? #59281073Gunning
R
20

You need use Stream to send file (archive) in a response, what is more you have to use appropriate Content-type in your response header.

There is an example function that do it:

const fs = require('fs');

// Where fileName is name of the file and response is Node.js Reponse. 
responseFile = (fileName, response) => {
    const filePath = "/path/to/archive.rar"; // or any file format

    // Check if file specified by the filePath exists
    fs.exists(filePath, function (exists) {
        if (exists) {
            // Content-type is very interesting part that guarantee that
            // Web browser will handle response in an appropriate manner.
            response.writeHead(200, {
                "Content-Type": "application/octet-stream",
                "Content-Disposition": "attachment; filename=" + fileName
            });
            fs.createReadStream(filePath).pipe(response);
            return;
        }
        response.writeHead(400, { "Content-Type": "text/plain" });
        response.end("ERROR File does not exist");
    });
}

The purpose of the Content-Type field is to describe the data contained in the body fully enough that the receiving user agent can pick an appropriate agent or mechanism to present the data to the user, or otherwise deal with the data in an appropriate manner.

"application/octet-stream" is defined as "arbitrary binary data" in RFC 2046, purpose of this content-type is to be saved to disk - it is what you really need.

"filename=[name of file]" specifies name of file which will be downloaded.

For more information please see this stackoverflow topic.

Rosenda answered 10/8, 2016 at 8:26 Comment(2)
how call request in client side give client side codeCollision
please note that 'exists' function is deprecated.Turnstone
E
5

This helped me. It will start downloading the file as soon as you hit the /your-route route.

app.get("/your-route", (req, res) => {

         let filePath = path.join(__dirname, "youe-file.whatever");

         res.download(filePath);
}

Yes download is also an express method.

Exaction answered 1/6, 2021 at 7:53 Comment(0)
A
4

Bit Late but express has a helper for this to make life easier.

app.get('/download', function(req, res){
  const file = `${__dirname}/path/to/folder/myfile.mp3`;
  res.download(file); // Set disposition and send it.
});
Ambience answered 7/5, 2021 at 21:41 Comment(0)
E
2

In case if you need to send gzipped on the fly file using Node.js natives only:

const fs = require('fs') // Node.js module
const zlib = require('zlib') // Node.js module as well

let sendGzip = (filePath, response) => {
    let headers = {
        'Connection': 'close', // intention
        'Content-Encoding': 'gzip',
        // add some headers like Content-Type, Cache-Control, Last-Modified, ETag, X-Powered-By
    }

    let file = fs.readFileSync(filePath) // sync is for readability
    let gzip = zlib.gzipSync(file) // is instance of Uint8Array
    headers['Content-Length'] = gzip.length // not the file's size!!!

    response.writeHead(200, headers)
    
    let chunkLimit = 16 * 1024 // some clients choke on huge responses
    let chunkCount = Math.ceil(gzip.length / chunkLimit)
    for (let i = 0; i < chunkCount; i++) {
        if (chunkCount > 1) {
            let chunk = gzip.slice(i * chunkLimit, (i + 1) * chunkLimit)
            response.write(chunk)
        } else {
            response.write(gzip)
        }
    }
    response.end()
}
Ebenezer answered 26/11, 2021 at 13:18 Comment(1)
Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Waftage

© 2022 - 2024 — McMap. All rights reserved.