🔨 Refactor: refactor the form uploader

This commit is contained in:
PiEgg
2025-06-02 17:16:52 +08:00
parent a928b4c8ab
commit 7bc70628f9
4 changed files with 147 additions and 102 deletions
+6 -2
View File
@@ -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"
},
}
+3 -100
View File
@@ -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
+124
View File
@@ -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()
+14
View File
@@ -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;
}
}