四好公路
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

132 lines
2.9 KiB

/**
* Module dependencies.
*/
var debug = require('debug')('koa-send');
var resolvePath = require('resolve-path');
var assert = require('assert');
var path = require('path');
var normalize = path.normalize;
var basename = path.basename;
var extname = path.extname;
var resolve = path.resolve;
var fs = require('mz/fs');
/**
* Expose `send()`.
*/
module.exports = send;
/**
* Send file at `path` with the
* given `options` to the koa `ctx`.
*
* @param {Context} ctx
* @param {String} path
* @param {Object} [opts]
* @return {Function}
* @api public
*/
function send(ctx, path, opts) {
assert(ctx, 'koa context required');
assert(path, 'pathname required');
opts = opts || {};
// options
debug('send "%s" %j', path, opts);
var root = opts.root ? normalize(resolve(opts.root)) : '';
var trailingSlash = '/' == path[path.length - 1];
path = path[0] == '/' ? path.slice(1) : path;
var index = opts.index;
var maxage = opts.maxage || opts.maxAge || 0;
var hidden = opts.hidden || false;
var gzip = opts.gzip || opts.gzip === undefined ? true : false;
return function *(){
var encoding = this.acceptsEncodings('gzip', 'deflate', 'identity');
// normalize path
path = decode(path);
if (-1 == path) return ctx.throw('failed to decode', 400);
// index file support
if (index && trailingSlash) path += index;
path = resolvePath(root, path);
// hidden file support, ignore
if (!hidden && isHidden(root, path)) return;
// serve gzipped file when possible
if (encoding === 'gzip' && gzip && (yield fs.exists(path + '.gz'))) {
path = path + '.gz';
ctx.set('Content-Encoding', 'gzip');
ctx.res.removeHeader('Content-Length');
}
// stat
try {
var stats = yield fs.stat(path);
// Format the path to serve static file servers
// and not require a trailing slash for directories,
// so that you can do both `/directory` and `/directory/`
if (stats.isDirectory()) {
if (opts.format) {
path += '/' + index;
} else {
return;
}
}
} catch (err) {
var notfound = ['ENOENT', 'ENAMETOOLONG', 'ENOTDIR'];
if (~notfound.indexOf(err.code)) return;
err.status = 500;
throw err;
}
// stream
ctx.set('Last-Modified', stats.mtime.toUTCString());
ctx.set('Content-Length', stats.size);
ctx.set('Cache-Control', 'max-age=' + (maxage / 1000 | 0));
ctx.type = type(path);
ctx.body = fs.createReadStream(path);
return path;
}
}
/**
* Check if it's hidden.
*/
function isHidden(root, path) {
path = path.substr(root.length).split('/');
for(var i = 0; i < path.length; i++) {
if(path[i][0] === '.') return true;
}
return false;
}
/**
* File type.
*/
function type(file) {
return extname(basename(file, '.gz'));
}
/**
* Decode `path`.
*/
function decode(path) {
try {
return decodeURIComponent(path);
} catch (err) {
return -1;
}
}