Skip to content

Commit

Permalink
Merge branch 'downloads'
Browse files Browse the repository at this point in the history
  • Loading branch information
tj committed Sep 20, 2010
2 parents f6ba793 + b763a88 commit 949803d
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 12 deletions.
15 changes: 14 additions & 1 deletion docs/guide.md
Expand Up @@ -84,7 +84,8 @@ Express supports the following settings out of the box:
* _views_ Root views directory defaulting to **CWD/views**
* _view engine_ Default view engine name for views rendered without extensions
* _view options_ An object specifying global view options
* _partials_ Root view partials directory defaulting to _views_/partials.
* _partials_ Root view partials directory defaulting to _views_/partials.
* _stream threshold_ Bytesize indicating when a file should be streamed for _res.sendfile()_ using _fs.ReadStream()_ and _sys.pump()_.

### Routing

Expand Down Expand Up @@ -456,6 +457,18 @@ support HTTP caching, and does not perform any security checks. This method is u
by _res.download()_ to transfer static files, and allows you do to so from outside of
the public directory, so suitable security checks should be applied.

This method accepts a callback which when given will be called on an exception, as well as when the transfer has completed. When a callback is not given, and the file has __not__ been streamed, _next(err)_ will be called on an exception.

res.sendfile(path, function(err, path){
if (err) {
// handle the error
} else {
console.log('transferred %s', path);
}
});

When the filesize exceeds the _stream threshold_ (defaulting to 32k), the file will be streamed using _fs.ReadStream_ and _sys.pump()_.

### res.download(file[, filename])

Transfer the given _file_ as an attachment with optional alternative _filename_.
Expand Down
42 changes: 31 additions & 11 deletions lib/express/response.js
Expand Up @@ -12,6 +12,7 @@
var fs = require('fs'),
http = require('http'),
path = require('path'),
pump = require('sys').pump,
utils = require('connect/utils'),
mime = require('connect/utils').mime,
Buffer = require('buffer').Buffer;
Expand Down Expand Up @@ -121,25 +122,44 @@ http.ServerResponse.prototype.send = function(body, headers, status){
* passing it as the first argument, or when the file is transferred,
* passing the path as the second argument.
*
* When the filesize is >= "stream threshold" (defaulting to 32k), the
* file will be streamed using an `fs.ReadStream` and `sys.pump()`.
*
* @param {String} path
* @param {Function} fn
* @api public
*/

http.ServerResponse.prototype.sendfile = function(path, fn){
var self = this;
fs.readFile(path, function(err, buf){
if (err) {
delete self.headers['Content-Disposition'];
if (fn) {
fn(err, path);
} else {
self.req.next(err);
}
var self = this,
streamThreshold = this.app.set('stream threshold') || 32 * 1024;

function error(err) {
delete self.headers['Content-Disposition'];
if (fn) {
fn(err, path);
} else {
self.req.next(err);
}
}

fs.stat(path, function(err, stat){
if (err) return error(err);
if (stat.size >= streamThreshold) {
var stream = fs.createReadStream(path);
self.contentType(path);
self.send(buf);
fn && fn(null, path);
self.header('Content-Length', stat.size);
self.writeHead(200, self.headers);
pump(stream, self, function(err){
fn && fn(err, path);
});
} else {
fs.readFile(path, function(err, buf){
if (err) return error(err);
self.contentType(path);
self.send(buf);
fn && fn(null, path);
});
}
});
};
Expand Down
151 changes: 151 additions & 0 deletions test/fixtures/large.json
@@ -0,0 +1,151 @@
{
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar"
}
6 changes: 6 additions & 0 deletions test/response.test.js
Expand Up @@ -185,6 +185,8 @@ module.exports = {
'test #sendfile()': function(assert){
var app = express.createServer();

app.set('stream threshold', 2 * 1024)

app.get('/*', function(req, res, next){
var file = req.params[0],
filePath = __dirname + '/fixtures/' + file;
Expand All @@ -208,6 +210,9 @@ module.exports = {
assert.response(app,
{ url: '/partials' },
{ body: 'Internal Server Error', status: 500 });
assert.response(app,
{ url: '/large.json' },
{ status: 200, headers: { 'Content-Length': 2535, 'Content-Type': 'application/json' }});
},

'test #download()': function(assert){
Expand Down Expand Up @@ -239,6 +244,7 @@ module.exports = {
{ url: '/json' },
{ body: '{"name":"tj"}', status: 200, headers: {
'Content-Type': 'application/json',
'Content-Length': '13',
'Content-Disposition': 'attachment; filename="account.json"'
}});

Expand Down

0 comments on commit 949803d

Please sign in to comment.