From 7bc70628f9968799c45e76d49c52860de5274d5b Mon Sep 17 00:00:00 2001 From: PiEgg Date: Mon, 2 Jun 2025 17:16:52 +0800 Subject: [PATCH] :hammer: Refactor: refactor the form uploader --- .vscode/settings.json | 8 +- src/main/server/index.ts | 103 +--------------- src/main/server/middlewares/formUploader.ts | 124 ++++++++++++++++++++ src/universal/types/global.d.ts | 14 +++ 4 files changed, 147 insertions(+), 102 deletions(-) create mode 100644 src/main/server/middlewares/formUploader.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 6c86e3c..dcf611f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "eslint.enable": true, "eslint.alwaysShowStatus": true, + "eslint.format.enable": true, "eslint.validate": [ "javascript", "javascriptreact", @@ -23,5 +24,8 @@ "source.fixAll.eslint": "explicit", "source.organizeImports": "never" }, - "prettier.enable": false -} + "prettier.enable": false, + "[typescript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint" + }, +} \ No newline at end of file diff --git a/src/main/server/index.ts b/src/main/server/index.ts index 66b5d00..3f3422d 100644 --- a/src/main/server/index.ts +++ b/src/main/server/index.ts @@ -4,64 +4,10 @@ import { handleResponse, ensureHTTPLink } from './utils' -import { dbPathDir } from 'apis/core/datastore/dbChecker' import picgo from '@core/picgo' import logger from '@core/picgo/logger' import axios from 'axios' -import multer from 'multer' -import path from 'path' -import fs from 'fs' - -// 扩展原生 IncomingMessage,添加 multer 字段 -declare module 'http' { - interface IncomingMessage { - files?: { - path: string; - originalname: string; - mimetype: string; - size: number; - [key: string]: any; - }[]; - body?: any; - } -} - -// Multer 中间件适配器类型 -export type MulterMiddleware = ( - req: http.IncomingMessage, - res: http.ServerResponse, - callback: (error?: any) => void -) => void; - -// 扩展 multer 函数的返回类型 -declare module 'multer' { - interface Multer { - array(fieldname: string, maxCount?: number): MulterMiddleware; - single(fieldname: string): MulterMiddleware; - fields(fields: { name: string; maxCount?: number }[]): MulterMiddleware; - none(): MulterMiddleware; - } -} - -const STORE_PATH = dbPathDir() -const formImagesPath = path.join(STORE_PATH, 'picgo-form-images') -if (!fs.existsSync(formImagesPath)) { - fs.mkdirSync(formImagesPath, { recursive: true }) -} - -// 使用 multer 处理表单文件 -const storage = multer.diskStorage({ - destination: function (req, file, cb) { - cb(null, formImagesPath) - }, - filename: function (req, file, cb) { - cb(null, Date.now() + path.extname(file.originalname)) - } -}) - -const upload = multer({ - storage -}) +import formUploader from './middlewares/formUploader' class Server { private httpServer: http.Server @@ -111,51 +57,8 @@ class Server { success: false } }) - } else if (request.headers['content-type'] && request.headers['content-type'].includes('multipart/form-data')) { - logger.info('[PicGo Server] handling file upload') - upload.array('files')(request, response, async (err) => { - if (err) { - logger.error('[PicGo Server] file upload error', err) - return handleResponse({ - response, - body: { - success: false, - message: 'File upload failed' - } - }) - } - - try { - const files = (request).files - if (!files || files.length === 0) { - return handleResponse({ - response, - body: { - success: false, - message: 'No files were uploaded' - } - }) - } - - const filePaths = files.map((file) => file.path) - logger.info('[PicGo Server] files uploaded: ' + filePaths.join(', ')) - - const handler = routers.getHandler(request.url!) - handler!({ - list: filePaths, - response - }) - } catch (err: any) { - logger.error('[PicGo Server] process upload files error', err) - handleResponse({ - response, - body: { - success: false, - message: 'Failed to process uploaded files' - } - }) - } - }) + } else if (formUploader.isFileUpload(request)) { + formUploader.handleFileUpload(request, response) } else { let body: string = '' let postObj: IObj diff --git a/src/main/server/middlewares/formUploader.ts b/src/main/server/middlewares/formUploader.ts new file mode 100644 index 0000000..1c130d1 --- /dev/null +++ b/src/main/server/middlewares/formUploader.ts @@ -0,0 +1,124 @@ +import http from 'http' +import multer from 'multer' +import path from 'path' +import fs from 'fs' +import { dbPathDir } from 'apis/core/datastore/dbChecker' +import logger from '@core/picgo/logger' +import { handleResponse } from '../utils' +import routers from '../routerManager' + +// Multer 错误类型定义 +export interface MulterError extends Error { + code: string + field?: string + storageErrors?: any[] +} + +// Multer 中间件适配器类型 +export type MulterMiddleware = ( + req: http.IncomingMessage, + res: http.ServerResponse, + callback: (error?: MulterError) => void +) => void; + +// 扩展 multer 函数的返回类型 +declare module 'multer' { + interface Multer { + array(fieldname: string, maxCount?: number): MulterMiddleware; + single(fieldname: string): MulterMiddleware; + fields(fields: { name: string; maxCount?: number }[]): MulterMiddleware; + none(): MulterMiddleware; + } +} + +class FormUploader { + private upload!: multer.Multer + private formImagesPath!: string + + constructor () { + this.initializeStorage() + this.setupMulter() + } + + private initializeStorage () { + const STORE_PATH = dbPathDir() + this.formImagesPath = path.join(STORE_PATH, 'picgo-form-images') + if (!fs.existsSync(this.formImagesPath)) { + fs.mkdirSync(this.formImagesPath, { recursive: true }) + } + } + + private setupMulter () { + const storage = multer.diskStorage({ + destination: (req: http.IncomingMessage, file: Express.Multer.File, cb: (error: Error | null, destination: string) => void) => { + cb(null, this.formImagesPath) + }, + filename: (req: http.IncomingMessage, file: Express.Multer.File, cb: (error: Error | null, filename: string) => void) => { + cb(null, Date.now() + path.extname(file.originalname)) + } + }) + + this.upload = multer({ + storage + }) + } + + /** + * 处理文件上传的中间件 + */ + public handleFileUpload = (request: http.IncomingMessage, response: http.ServerResponse): void => { + logger.info('[PicGo Server] handling file upload') + + this.upload.array('files')(request, response, async (err?: MulterError) => { + if (err) { + logger.error('[PicGo Server] file upload error', err) + return handleResponse({ + response, + body: { + success: false, + message: 'File upload failed' + } + }) + } + + try { + const files = request.files + if (!files || files.length === 0) { + return handleResponse({ + response, + body: { + success: false, + message: 'No files were uploaded' + } + }) + } + const filePaths = files.map((file) => file.path) + logger.info('[PicGo Server] files uploaded: ' + filePaths.join(', ')) + + const handler = routers.getHandler(request.url!) + handler!({ + list: filePaths, + response + }) + } catch (err: any) { + logger.error('[PicGo Server] process upload files error', err) + handleResponse({ + response, + body: { + success: false, + message: 'Failed to process uploaded files' + } + }) + } + }) + } + + /** + * 检查请求是否为文件上传 + */ + public isFileUpload (request: http.IncomingMessage): boolean { + return !!(request.headers['content-type'] && request.headers['content-type'].includes('multipart/form-data')) + } +} + +export default new FormUploader() diff --git a/src/universal/types/global.d.ts b/src/universal/types/global.d.ts index d2deffe..d6e73a2 100644 --- a/src/universal/types/global.d.ts +++ b/src/universal/types/global.d.ts @@ -7,3 +7,17 @@ declare var notificationList: IAppNotification[] declare module 'epipebomb' { export default function epipebomb(stream: NodeJS.Process['stdout'], callback: () => void): void } + +// 扩展原生 IncomingMessage,添加 multer 字段 +declare module 'http' { + interface IncomingMessage { + files?: { + path: string; + originalname: string; + mimetype: string; + size: number; + [key: string]: any; + }[]; + body?: any; + } +}