一个网盘类型的项目,肯定离不开文件系统。用于快速验证流程,这里使用了 node koa 作为文件系统服务,运行在本地。
初始化项目
照着 koa 官方文档来,当然是没有错的。但是为了更好的开发,我们需要引入更多的依赖,后续逐步添加也可以。
添加 ts、nodemon 依赖
npm install --save-dev @types/koa typescript ts-node nodemon
// tsconfig.json
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
// nodemon.json
{
"ignore": [
"*.log",
"data.json",
"dist/",
"node_modules/",
"src/tests/"
],
"watch": [
"src"
],
"ext": "js,ts"
}
添加 koa 常用几个依赖
npm install koa-bodyparser koa-router koa-static log4js
npm install --save-dev @types/koa-bodyparser @types/koa-router @types/koa-static
组织目录结构
controllers 用于处理 routes 的请求,DB 相关操作,复杂业务逻辑在 services 中,utils 用于存放一些工具函数,types 用于存放类型声明。middles 可以用于存放统一处理路由等。
.
├── __test__
│ └── utils.test.ts # 测试文件
├── config # 配置文件
├── controllers # 控制器
│ ├── dir.ts
├── index.ts # 入口文件
├── middles # 中间件
│ ├── format.ts
│ └── reqLog.ts
├── public # 静态文件
│ ├── index.html
├── routes # 路由
├── services # 服务
├── types # 类型声明
└── utils # 工具函数
项目开发
基本内容准备完毕之后,进入开发之前,因为我希望能够监听本地文件的变化,及时的通知到客户端,所以需要使用 ws 模块。
npm install ws chokidar fs-extra
npm install --save-dev @types/ws @types/chokidar @types/fs-extra
使用 ws
// ...
// 启动服务
const server = app.listen(PORT, () => {
logger.info(`Server is running on http://localhost:${PORT}`);
});
initWs(server);
function initWs (server: Server) {
// 初始化 WebSocket 服务
new WSServerController(server);
}
路由编写,使用 Joi 校验参数
这里简单给出一个 post 请求 Joi 校验的例子。
// router.post('/dir', postDir);
const postDirSchema = Joi.object({
root: Joi.string().required(),
name: Joi.string().required(),
})
export const postDir = (ctx: Context) => {
const { error, value } = postDirSchema.validate(ctx.request.body);
if (error) {
ctx.status = 400;
ctx.body = {
code: 400,
message: error.details[0].message,
};
return;
}
const { root, name } = value;
try {
// 检查路径是否存在
// 检查文件夹是否存在
// 创建文件夹
fs.mkdirSync(path.join(root, name));
ctx.body = {};
} catch (error) {
return;
}
}
使用 @koa/multer 处理文件上传
需要注意的是在客户端上传文件的时候,目标 path 或者其他的参数,通过 body 获取需要写在 formData file 参数的前面。否则文件存储完成才能解析到 body 中的数据。 因为 multer 在做文件接收的时候,为了尽快的将文件写入磁盘,会按照顺序处理传递过来的文件流。
export const upload = multer({
storage: multer.diskStorage({
destination: (req: any, file, cb) => {
const targetPath = req.body?.path || '';
const uploadDir = targetPath; //path.join(uploadPath, targetPath);
cb(null, uploadDir);
},
filename: (req: IncomingMessage & {
body: {
path?: string;
};
}, file, cb) => {
const targetPath = req.body?.path || '';
const uploadDir = targetPath; //path.join(uploadPath, targetPath);
const originalname = file.originalname;
const ext = path.extname(originalname);
const baseName = path.basename(originalname, ext);
let fileName = originalname;
let count = 1;
// 检查文件是否存在,如果存在则增加后缀
while (fs.existsSync(path.join(uploadDir, fileName))) {
fileName = `${baseName}_${count}${ext}`;
count++;
}
cb(null, fileName);
}
})
})
构建
到这里简单的 koa 项目就开发完成了。由于需要被 Electron 项目 fork启动,所以需要构建。
# npm run build
npx tsc