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 += ``;
- }
- 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静态服务器
+
+
+
+
+

+
+
抱歉,你访问的路径不存在
+
您要找的页面没有找到,请返回首页继续浏览
+
+
+
+
+`
\ 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