waffle
Version:
シンプルなWEBアプリケーションフレームワークです。(ALL YOUR NODE ARE BELONG TO US)
161 lines (148 loc) • 4.89 kB
JavaScript
/*
* Copyright 2012 Katsunori Koyanagi
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
/**
* @overview 外部から不可視であるファイルをダウンロードされるためのレンダラです。
*/
;
var lang = require("../utils/Lang");
var mime = require("mime");
var fs = require("fs");
var Stream = require("stream");
/**
* 外部から不可視であるファイルをダウンロードされるためのレンダラです。
* <p>
* このレンダラをコントローラで指定する場合のフォーマットは<prefix>となります。
* データにはファイル情報を指定してください。ダウンロードさせる内容が不確定の場合は、404エラーとなります。
* ファイル情報は以下のメンバーを持つオブジェクトである必要があります。
* </p>
* <table>
* <tr>
* <th>メンバ名</th>
* <th>型</th>
* <th>必須</th>
* <th>説明</th>
* </tr>
* <tr>
* <td>data</td>
* <td>Node.jsのReadableStreamもしくは、Node.jsのBufferもしくはString</td>
* <td>true</td>
* <td>Stringが指定された場合は絶対パスのファイル名とみなし、そのファイルをダウンロードの対象とします。</td>
* </tr>
* <tr>
* <td>contentType</td>
* <td>String</td>
* <td>false</td>
* <td>dataでファイル名が指定されている場合、fileNameが指定されている場合は、自動的にそのファイル名に基づきContent-Typeが解決されますが、
* オーバーライドしたい場合、ファイル名を解決するパラメータが存在しない場合は、指定する必要があります。省略時は、"application/octet-stream"になります。</td>
* </tr>
* <tr>
* <td>fileName</td>
* <td>String</td>
* <td>false</td>
* <td>ブラウザがファイルを保存する際に使用されるファイル名です。省略した場合はdataにファイル名が指定されている場合のみ自動で解決されます。</td>
* </tr>
* </table>
*
* @example <code>
* var waffle = require("waffle");
* config.renderer.register("file", waffle.renderers.file);
* </code>
*
* @name Renderers#file
*/
exports.render = function(context, param, data, callback) {
if (!data) {
context.error(404);
return;
}
var content = data.data;
if (content == null || content == undefined) {
context.error(404);
return;
}
var onStat = function(error, stat) {
if (error) {
context.error(500, error);
return;
}
if (stat != null && !stat.isFile()) {
context.error(404);
return;
}
var len = stat == null ? -1 : stat.size;
var fileName = null;
if (lang.isString(content)) {
fileName = content;
}
if (data.fileName) {
fileName = data.fileName;
}
if (fileName == null) {
fileName = "";
}
var contentType = null;
if (data.contentType) {
contentType = data.contentType;
}
if (!contentType) {
contentType = mime.lookup(fileName);
}
var stream = null;
if (lang.isString(content)) {
stream = new fs.ReadStream(content);
} else if (lang.isNumber(content)) {
stream = new fs.ReadStream(null, {
fd : content
});
} else if (content instanceof Stream) {
stream = content;
}
fileName = fileName ? ("attachment; filename=\""
+ fs.basename(fileName) + "\"") : ("attachment");
context.res.statusCode = 200;
context.res.setHeader("Content-Type", contentType);
context.res.setHeader("Content-Disposition", fileName);
if (len > -1) {
context.res.setHeader("Content-Length", len);
}
var len0 = 0;
var handler = function(obj) {
if (obj instanceof Buffer) {
context.res.write(obj);
} else if (lang.isError(obj)) {
stream.removeAllListeners();
context.error(500, error);
} else {
stream.removeAllListeners();
console.log(fs.fstatSync(stream.fd));
callback();
}
};
stream.on("end", handler);
stream.on("data", handler);
stream.on("error", handler);
};
if (lang.isString(content)) {
fs.stat(content, onStat);
} else if (lang.isNumber(content)) {
fs.fstat(content, onStat);
} else if (content instanceof Stream) {
onStat(null, null);
} else {
context.error(404);
return;
}
};