Node.js send file in response

node.js

node.js Problem Overview


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.

node.js Solutions


Solution 1 - node.js

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/

Solution 2 - node.js

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.

Solution 3 - node.js

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.

Solution 4 - node.js

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.
});

Solution 5 - node.js

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()
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionandreiView Question on Stackoverflow
Solution 1 - node.jsMichelle TilleyView Answer on Stackoverflow
Solution 2 - node.jsMikePtrView Answer on Stackoverflow
Solution 3 - node.jsDebu ShinobiView Answer on Stackoverflow
Solution 4 - node.jsAwais AyubView Answer on Stackoverflow
Solution 5 - node.jsKonstantin ChemshirovView Answer on Stackoverflow