UNPKG

cooperation

Version:
163 lines (139 loc) 4.96 kB
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 + "端口") };