cooperation
Version:
163 lines (139 loc) • 4.96 kB
JavaScript
const http = require("http");
const fs = require("fs");
const path = require("path");
const url = require("url");
const NAME = path.basename(__filename, '.js');
const mimeNames = {
".mp3": "audio/mpeg",
".mp4": "video/mp4",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "application/x-png",
".gif": "image/gif",
".ogg": "application/ogg",
".ogv": "video/ogg",
".oga": "audio/ogg",
".wav": "audio/x-wav",
".webm": "video/webm",
".html": "text/html",
".css": "text/css",
".js": "application/javascript",
".json": "application/json"
};
function sendResponse(response, responseStatus, responseHeaders, readable) {
response.writeHead(responseStatus, responseHeaders);
if (!readable)
response.end();
else
readable.on("open", () => readable.pipe(response));
return null;
}
function getMimeNameFromExt(ext) {
let result = mimeNames[ext.toLowerCase()];
// 最好给一个默认值
if (!result)
result = "application/octet-stream";
return result;
}
function readRangeHeader(range, totalLength) {
/*
* Example of the method 'split' with regular expression.
*
* Input: bytes=100-200
* Output: [null, 100, 200, null]
*
* Input: bytes=-200
* Output: [null, null, 200, null]
*/
if (!range || !range.length)
return null;
let array = range.split(/bytes=([0-9]*)-([0-9]*)/);
let start = parseInt(array[1]);
let end = parseInt(array[2]);
let result = {
Start: isNaN(start) ? 0 : start,
End: isNaN(end) ? (totalLength - 1) : end
};
if (!isNaN(start) && isNaN(end)) {
result.Start = start;
result.End = totalLength - 1;
}
if (isNaN(start) && !isNaN(end)) {
result.Start = totalLength - end;
result.End = totalLength - 1;
}
return result;
}
module.exports = ({
port = 80,//端口
dir = ['/']
} = {}) => {
http.createServer((request, response) => {
//下面主要是GET类的
// 只允许GET请求
if (request.method !== 'GET') {
sendResponse(response, 405, {'Allow': 'GET'}, null);
return null;
}
let rqePath = url.parse(request.url, true, true).pathname.split('/').join(path.sep);
let filename = '';
//同步检查文件状态
let stat = null;
if (dir.length === 1) {
filename = dir[0] + rqePath;
stat =fs.statSync(filename);
// 检查文件是否存在
if (!stat.isFile()) {
sendResponse(response, 404, null, null);
return null;
}
} else {
let has = false;
dir.some(v => {
filename = v + rqePath;
stat =fs.statSync(filename);
//TODO 需要优化
if (!stat.isFile()) {
has = true;
}
return has;
});
if (!has) {
sendResponse(response, 404, null, null);
return null;
}
}
let responseHeaders = {};
let rangeRequest = readRangeHeader(request.headers['range'], stat.size);
//不存在Range头,直接下载
if (rangeRequest == null) {
responseHeaders['Content-Type'] = getMimeNameFromExt(path.extname(filename));
responseHeaders['Content-Length'] = stat.size; // File size.
responseHeaders['Accept-Ranges'] = 'bytes';
//直接返回图片
sendResponse(response, 200, responseHeaders, fs.createReadStream(filename));
return null;
}
//存在Range头,分段下载
let start = rangeRequest.Start;
let end = rangeRequest.End;
// If the range can't be fulfilled.
if (start >= stat.size || end >= stat.size) {
// Indicate the acceptable range.
responseHeaders['Content-Range'] = 'bytes */' + stat.size; // File size.
// Return the 416 'Requested Range Not Satisfiable'.
sendResponse(response, 416, responseHeaders, null);
return null;
}
// Indicate the current range.
responseHeaders['Content-Range'] = 'bytes ' + start + '-' + end + '/' + stat.size;
responseHeaders['Content-Length'] = start === end ? 0 : (end - start + 1);
responseHeaders['Content-Type'] = getMimeNameFromExt(path.extname(filename));
responseHeaders['Accept-Ranges'] = 'bytes';
responseHeaders['Cache-Control'] = 'no-cache';
// Return the 206 'Partial Content'.
sendResponse(response, 206,
responseHeaders, fs.createReadStream(filename, {start: start, end: end}));
}).listen(port);
console.log(NAME + ":监听了" + port + "端口")
};