From 1c0a570d74e318bc7df0fbbc69f701bae23fc12b Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Tue, 25 Apr 2023 15:12:28 +0800 Subject: [PATCH] [+] FileServer [+] FileServer --- Luban/FileServer/.gitignore | 8 + Luban/FileServer/FileSys.js | 98 ----- Luban/FileServer/README.md | 42 +++ Luban/FileServer/bin/app.js | 26 ++ Luban/FileServer/index.js | 3 + Luban/FileServer/instal.bat | 1 + Luban/FileServer/package.json | 30 ++ Luban/FileServer/src/mime.js | 12 + Luban/FileServer/src/static-server.js | 334 ++++++++++++++++++ Luban/FileServer/src/templates/404.js | 44 +++ Luban/FileServer/src/templates/default.js | 93 +++++ Luban/FileServer/src/templates/images/404.png | Bin 0 -> 12644 bytes Luban/FileServer/src/templates/index.js | 7 + Luban/FileServer/start.bat | 2 +- Luban/FileServer/start.sh | 1 - 15 files changed, 601 insertions(+), 100 deletions(-) create mode 100644 Luban/FileServer/.gitignore delete mode 100644 Luban/FileServer/FileSys.js create mode 100644 Luban/FileServer/README.md create mode 100644 Luban/FileServer/bin/app.js create mode 100644 Luban/FileServer/index.js create mode 100644 Luban/FileServer/instal.bat create mode 100644 Luban/FileServer/package.json create mode 100644 Luban/FileServer/src/mime.js create mode 100644 Luban/FileServer/src/static-server.js create mode 100644 Luban/FileServer/src/templates/404.js create mode 100644 Luban/FileServer/src/templates/default.js create mode 100644 Luban/FileServer/src/templates/images/404.png create mode 100644 Luban/FileServer/src/templates/index.js delete mode 100644 Luban/FileServer/start.sh diff --git a/Luban/FileServer/.gitignore b/Luban/FileServer/.gitignore new file mode 100644 index 00000000..a52d3c3c --- /dev/null +++ b/Luban/FileServer/.gitignore @@ -0,0 +1,8 @@ +node_modules +*.swap +.idea +.DS_Store +*.log +.vscode +*-lock.json +AssetsRoot \ No newline at end of file diff --git a/Luban/FileServer/FileSys.js b/Luban/FileServer/FileSys.js deleted file mode 100644 index c48b7354..00000000 --- a/Luban/FileServer/FileSys.js +++ /dev/null @@ -1,98 +0,0 @@ -//----------------------------------------------------------------------- -// Copyright (c) TEngine. All rights reserved. -// Author: TangXiao -// Date: 2022/5/14 16:29:13 -//----------------------------------------------------------------------- -const http = require("http"); -const url = require("url"); -const fs = require("fs"); -const path = require("path"); -const mime = { - css: "text/css", - gif: "image/gif", - html: "text/html", - ico: "image/x-icon", - jpeg: "image/jpeg", - jpg: "image/jpeg", - js: "text/javascript", - json: "application/json", - pdf: "application/pdf", - png: "image/png", - svg: "image/svg+xml", - swf: "application/x-shockwave-flash", - tiff: "image/tiff", - txt: "text/plain", - wav: "audio/x-wav", - wma: "audio/x-ms-wma", - wmv: "video/x-ms-wmv", - xml: "text/xml", -}; -const port = 8088; - -const httpServer = http.createServer((request, response) => { - const requestUrl = request.url; - let pathName = url.parse(requestUrl).pathname; - - // 对路径解码,防止中文乱码 - pathName = decodeURI(pathName); - - // 绝对路径 - const filePath = path.resolve(__dirname + pathName); - - // 扩展名 - let ext = path.extname(pathName); - ext = ext ? ext.slice(1) : "unknown"; - - // 未知的类型一律用"text/plain"类型 - const contentType = mime[ext] || "text/plain"; - - // fs.stat()方法用于判断给定的路径是否存在 - fs.stat(filePath, (err, stats) => { - // 路径不存在,则返回404 - if (err) { - response.writeHead(404, { "content-type": "text/html" }); - response.end("

404 Not Found

"); - } - // 如果是文件 - if (!err && stats.isFile()) { - response.writeHead(200, { "content-type": contentType }); - // 建立流对象,读文件 - const stream = fs.createReadStream(filePath); - // 错误处理 - stream.on("error", function() { - response.writeHead(500, { "content-type": contentType }); - - response.end("

500 Server Error

"); - }); - // 读取文件 - stream.pipe(response); - //response.end(); // 这个地方有坑,加了会关闭对话,看不到内容了 - } - // 如果是路径 - if (!err && stats.isDirectory()) { - let html = " "; - // 读取该路径下文件 - fs.readdir(filePath, (err, files) => { - if (err) { - response.writeHead(500, { "content-type": contentType }); - response.end("

路径读取失败!

"); - } else { - for (const file of files) { - if (file === "index.html") { - response.writeHead(200, { "content-type": "text/html" }); - response.end(file); - break; - } - html += `
${file}
`; - } - response.writeHead(200, { "content-type": "text/html" }); - response.end(html); - } - }); - } - }); -}); - -httpServer.listen(port, function() { - console.log(`File Service: ${port}`); -}); \ No newline at end of file diff --git a/Luban/FileServer/README.md b/Luban/FileServer/README.md new file mode 100644 index 00000000..4f955671 --- /dev/null +++ b/Luban/FileServer/README.md @@ -0,0 +1,42 @@ +## 使用node搭建静态资源服务器 + +### 安装 + +```bash +npm install yumu-static-server -g +``` + +### 使用 + +```bash +server # 会在当前目录下启动一个静态资源服务器,默认端口为8080 + +server -p[port] 3000 # 会在当前目录下启动一个静态资源服务器,端口为3000 + +server -i[index] index.html # 设置文件夹在默认加载的文件 + +server -c[charset] UTF-8 # 设置文件默认加载的字符编码 + +server -cors # 开启文件跨域 + +server -h[https] # 开启https服务 + +server --openindex # 是否打开默认页面 + +server --no-openbrowser # 关闭自动打开浏览器 +``` + +### 基本功能 + +1. 启动静态资源服务器 +2. 端口可配置 +3. 字符编码可配置 +4. 文件夹下默认加载文件可配置 +5. 是否跨域可配置 +6. 开启https服务 + +### TODO + +- [x] 引入handlerbars编译模板 +- [x] 支持文件是否跨域 +- [x] 支持https服务 diff --git a/Luban/FileServer/bin/app.js b/Luban/FileServer/bin/app.js new file mode 100644 index 00000000..bc2a45d5 --- /dev/null +++ b/Luban/FileServer/bin/app.js @@ -0,0 +1,26 @@ +const StaticServer = require('../src/static-server'); + +const options = require('yargs') + .option('p', { alias: 'port', describe: '设置服务启动的端口号', type: 'number' }) + .option('i', { alias: 'index', describe: '设置默认打开的主页', type: 'string' }) + .option('c', { alias: 'charset', describe: '设置文件的默认字符集', type: 'string' }) + .option('o', { alias: 'openindex', describe: '是否打开默认页面', type: 'boolean' }) + .option('h', { alias: 'https', describe: '是否启用https服务', type: 'boolean' }) + .option('cors', { describe: '是否开启文件跨域', type: 'boolean' }) + .option('openbrowser', { describe: '是否默认打开浏览器', type: 'boolean' }) + + // 默认参数 + .default('openbrowser', true) + // .default('https', true) + .default('port', 8080) + .default('index', 'index.html') + .default('openindex', 'index.html') + .default('charset', 'UTF-8') + + .help() + .alias('?', 'help') + + .argv; + +const server = new StaticServer(options); +server.start(); \ No newline at end of file diff --git a/Luban/FileServer/index.js b/Luban/FileServer/index.js new file mode 100644 index 00000000..f113fe8b --- /dev/null +++ b/Luban/FileServer/index.js @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +module.exports = require('./bin/app.js'); diff --git a/Luban/FileServer/instal.bat b/Luban/FileServer/instal.bat new file mode 100644 index 00000000..ad36db1c --- /dev/null +++ b/Luban/FileServer/instal.bat @@ -0,0 +1 @@ +npm install yumu-static-server -g \ No newline at end of file diff --git a/Luban/FileServer/package.json b/Luban/FileServer/package.json new file mode 100644 index 00000000..73cdb9f0 --- /dev/null +++ b/Luban/FileServer/package.json @@ -0,0 +1,30 @@ +{ + "name": "static-server", + "version": "0.0.1", + "description": "使用node搭建静态资源服务器", + "main": "index.js", + "scripts": { + "dev": "supervisor bin/app.js", + "start": "npm run dev" + }, + "bin": { + "server": "index.js" + }, + "author": "Alex", + "license": "ISC", + "keywords": [ + "static-server", + "server" + ], + "dependencies": { + "chalk": "^2.3.2", + "handlebars": "^4.0.11", + "mime": "^2.2.0", + "open": "^7.1.0", + "pem": "^1.12.5", + "yargs": "^6.6.0" + }, + "devDependencies": { + "supervisor": "^0.12.0" + } +} \ No newline at end of file diff --git a/Luban/FileServer/src/mime.js b/Luban/FileServer/src/mime.js new file mode 100644 index 00000000..967897d5 --- /dev/null +++ b/Luban/FileServer/src/mime.js @@ -0,0 +1,12 @@ +const path = require('path'); +const mime = require('mime'); + +const lookup = (pathName) => { + let ext = path.extname(pathName); + ext = ext.split('.').pop(); + return mime.getType(ext) || mime.getType('txt'); +} + +module.exports = { + lookup +}; diff --git a/Luban/FileServer/src/static-server.js b/Luban/FileServer/src/static-server.js new file mode 100644 index 00000000..ca2c5113 --- /dev/null +++ b/Luban/FileServer/src/static-server.js @@ -0,0 +1,334 @@ +const http = require('http'); +const https = require('https'); +const path = require('path'); +const fs = require('fs'); +const url = require('url'); +const zlib = require('zlib'); +const chalk = require('chalk'); +const os = require('os'); +const open = require("open"); +const Handlebars = require('handlebars'); +const pem = require('pem'); +const mime = require('./mime'); +const Template = require('./templates'); + +const _defaultTemplate = Handlebars.compile(Template.page_dafault); +const _404TempLate = Handlebars.compile(Template.page_404); + +const hasTrailingSlash = url => url[url.length - 1] === '/'; + +const ifaces = os.networkInterfaces(); + +class StaticServer { + constructor(options) { + this.port = options.port; + this.indexPage = options.index; + this.openIndexPage = options.openindex; + this.openBrowser = options.openbrowser; + this.charset = options.charset; + this.cors = options.cors; + this.protocal = options.https ? 'https' : 'http'; + this.zipMatch = '^\\.(css|js|html)$'; + } + + /** + * 响应错误 + * + * @param {*} err + * @param {*} res + * @returns + * @memberof StaticServer + */ + respondError(err, res) { + res.writeHead(500); + return res.end(err); + } + + /** + * 响应404 + * + * @param {*} req + * @param {*} res + * @memberof StaticServer + */ + respondNotFound(req, res) { + res.writeHead(404, { + 'Content-Type': 'text/html' + }); + const html = _404TempLate(); + res.end(html); + } + + respond(pathName, req, res) { + fs.stat(pathName, (err, stat) => { + if (err) return respondError(err, res); + this.responseFile(stat, pathName, req, res); + }); + } + + /** + * 判断是否需要解压 + * + * @param {*} pathName + * @returns + * @memberof StaticServer + */ + shouldCompress(pathName) { + return path.extname(pathName).match(this.zipMatch); + } + + /** + * 解压文件 + * + * @param {*} readStream + * @param {*} req + * @param {*} res + * @returns + * @memberof StaticServer + */ + compressHandler(readStream, req, res) { + const acceptEncoding = req.headers['accept-encoding']; + if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) { + return readStream; + } else if (acceptEncoding.match(/\bgzip\b/)) { + res.setHeader('Content-Encoding', 'gzip'); + return readStream.pipe(zlib.createGzip()); + } + } + + /** + * 响应文件路径 + * + * @param {*} stat + * @param {*} pathName + * @param {*} req + * @param {*} res + * @memberof StaticServer + */ + responseFile(stat, pathName, req, res) { + // 设置响应头 + res.setHeader('Content-Type', `${mime.lookup(pathName)}; charset=${this.charset}`); + res.setHeader('Accept-Ranges', 'bytes'); + + // 添加跨域 + if (this.cors) res.setHeader('Access-Control-Allow-Origin', '*'); + + let readStream; + readStream = fs.createReadStream(pathName); + if (this.shouldCompress(pathName)) { // 判断是否需要解压 + readStream = this.compressHandler(readStream, req, res); + } + readStream.pipe(res); + } + + /** + * 响应重定向 + * + * @param {*} req + * @param {*} res + * @memberof StaticServer + */ + respondRedirect(req, res) { + const location = req.url + '/'; + res.writeHead(301, { + 'Location': location, + 'Content-Type': 'text/html' + }); + const html = _defaultTemplate({ + htmlStr: `Redirecting to ${location}`, + showFileList: false + }) + res.end(html); + } + + /** + * 响应文件夹路径 + * + * @param {*} pathName + * @param {*} req + * @param {*} res + * @memberof StaticServer + */ + respondDirectory(pathName, req, res) { + const indexPagePath = path.join(pathName, this.indexPage); + // 如果文件夹下存在index.html,则默认打开 + if (this.openIndexPage && fs.existsSync(indexPagePath)) { + this.respond(indexPagePath, req, res); + } else { + fs.readdir(pathName, (err, files) => { + if (err) { + respondError(err, res); + } + const requestPath = url.parse(req.url).pathname; + const fileList = []; + files.forEach(fileName => { + let itemLink = path.join(requestPath, fileName); + let isDirectory = false; + const stat = fs.statSync(path.join(pathName, fileName)); + if (stat && stat.isDirectory()) { + itemLink = path.join(itemLink, '/'); + isDirectory = true; + } + fileList.push({ + link: itemLink, + name: fileName, + isDirectory + }); + }); + // 排序,目录在前,文件在后 + fileList.sort((prev, next) => { + if (prev.isDirectory && !next.isDirectory) { + return -1; + } + return 1; + }); + res.writeHead(200, { + 'Content-Type': 'text/html' + }); + const html = _defaultTemplate({ + requestPath, + fileList, + showFileList: true + }) + res.end(html); + }); + } + } + + /** + * 路由处理 + * + * @param {*} pathName + * @param {*} req + * @param {*} res + * @memberof StaticServer + */ + routeHandler(pathName, req, res) { + const realPathName = pathName.split('?')[0]; + fs.stat(realPathName, (err, stat) => { + this.logGetInfo(err, pathName); + if (!err) { + const requestedPath = url.parse(req.url).pathname; + // 检查url + // 如果末尾有'/',且是文件夹,则读取文件夹 + // 如果是文件夹,但末尾没'/',则重定向至'xxx/' + // 如果是文件,则判断是否是压缩文件,是则解压,不是则读取文件 + if (hasTrailingSlash(requestedPath) && stat.isDirectory()) { + this.respondDirectory(realPathName, req, res); + } else if (stat.isDirectory()) { + this.respondRedirect(req, res); + } else { + this.respond(realPathName, req, res); + } + } else { + this.respondNotFound(req, res); + } + }); + } + + /** + * 打印ip地址 + * + * @memberof StaticServer + */ + logUsingPort() { + const me = this; + console.log(`${chalk.yellow(`Starting up your server\nAvailable on:`)}`); + Object.keys(ifaces).forEach(function (dev) { + ifaces[dev].forEach(function (details) { + if (details.family === 'IPv4') { + console.log(` ${me.protocal}://${details.address}:${chalk.green(me.port)}`); + } + }); + }); + console.log(`${chalk.cyan(Array(50).fill('-').join(''))}`); + } + + /** + * 打印占用端口 + * + * @param {*} oldPort + * @param {*} port + * @memberof StaticServer + */ + logUsedPort(oldPort, port) { + const me = this; + console.log(`${chalk.red(`The port ${oldPort} is being used, change to port `)}${chalk.green(me.port)} `); + } + + /** + * 打印https证书友好提示 + * + * @memberof StaticServer + */ + logHttpsTrusted() { + console.log(chalk.green('Currently is using HTTPS certificate (Manually trust it if necessary)')); + } + + + /** + * 打印路由路径输出 + * + * @param {*} isError + * @param {*} pathName + * @memberof StaticServer + */ + logGetInfo(isError, pathName) { + if (isError) { + console.log(chalk.red(`404 ${pathName}`)); + } else { + console.log(chalk.cyan(`200 ${pathName}`)); + } + } + + startServer(keys) { + const me = this; + let isPostBeUsed = false; + const oldPort = me.port; + const protocal = me.protocal === 'https' ? https : http; + const options = me.protocal === 'https' ? { key: keys.serviceKey, cert: keys.certificate } : null; + const callback = (req, res) => { + const pathName = path.join(process.cwd(), path.normalize(decodeURI(req.url))); + me.routeHandler(pathName, req, res); + }; + const params = [callback]; + if (me.protocal === 'https') params.unshift(options); + const server = protocal.createServer(...params).listen(me.port); + server.on('listening', function () { // 执行这块代码说明端口未被占用 + if (isPostBeUsed) { + me.logUsedPort(oldPort, me.port); + } + me.logUsingPort(); + if (me.openBrowser) { + open(`${me.protocal}://127.0.0.1:${me.port}`); + } + }); + + server.on('error', function (err) { + if (err.code === 'EADDRINUSE') { // 端口已经被使用 + isPostBeUsed = true; + me.port = parseInt(me.port) + 1; + server.listen(me.port); + } else { + console.log(err); + } + }) + } + + start() { + const me = this; + if (this.protocal === 'https') { + pem.createCertificate({ days: 1, selfSigned: true }, function (err, keys) { + if (err) { + throw err + } + me.logHttpsTrusted(); + me.startServer(keys); + }) + } else { + me.startServer(); + } + } +} + +module.exports = StaticServer; \ No newline at end of file diff --git a/Luban/FileServer/src/templates/404.js b/Luban/FileServer/src/templates/404.js new file mode 100644 index 00000000..62d60971 --- /dev/null +++ b/Luban/FileServer/src/templates/404.js @@ -0,0 +1,44 @@ +module.exports = ` + + + + + + + node静态服务器 + + + +
+ not found +
+

抱歉,你访问的路径不存在

+

您要找的页面没有找到,请返回首页继续浏览

+
+
+ + +` \ No newline at end of file diff --git a/Luban/FileServer/src/templates/default.js b/Luban/FileServer/src/templates/default.js new file mode 100644 index 00000000..4f389f6b --- /dev/null +++ b/Luban/FileServer/src/templates/default.js @@ -0,0 +1,93 @@ +module.exports = ` + + + + + + + node静态服务器 + + + +
+

当前目录:{{requestPath}}

+ {{#if showFileList}} + + {{else}} + {{htmlStr}} + {{/if}} +
+ + + +`; \ No newline at end of file diff --git a/Luban/FileServer/src/templates/images/404.png b/Luban/FileServer/src/templates/images/404.png new file mode 100644 index 0000000000000000000000000000000000000000..93cd1bb824f9d8e50ac20f0b0f6ce4c33824f816 GIT binary patch literal 12644 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 BQn3I4 literal 0 HcmV?d00001 diff --git a/Luban/FileServer/src/templates/index.js b/Luban/FileServer/src/templates/index.js new file mode 100644 index 00000000..4b51b700 --- /dev/null +++ b/Luban/FileServer/src/templates/index.js @@ -0,0 +1,7 @@ +const page_dafault = require('./default'); +const page_404 = require('./404'); + +module.exports = { + page_dafault, + page_404 +}; \ No newline at end of file diff --git a/Luban/FileServer/start.bat b/Luban/FileServer/start.bat index babde2c4..13cd1fa7 100644 --- a/Luban/FileServer/start.bat +++ b/Luban/FileServer/start.bat @@ -1 +1 @@ -node FileSys.js \ No newline at end of file +server \ No newline at end of file diff --git a/Luban/FileServer/start.sh b/Luban/FileServer/start.sh deleted file mode 100644 index babde2c4..00000000 --- a/Luban/FileServer/start.sh +++ /dev/null @@ -1 +0,0 @@ -node FileSys.js \ No newline at end of file