const multer = require('multer'); const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); // 用於生成隨機字串 const storage = multer.diskStorage({ destination: (req, file, cb) => { const { dbname, typeid } = req.params; const uploadPath = path.join(__dirname, `../../public/upload/${dbname}/${typeid}`); fs.mkdirSync(uploadPath, { recursive: true }); cb(null, uploadPath); }, filename: (req, file, cb) => { //下行程式會自動加入日期 tag 在檔名前, 目前先取消 2026/01/10 //const dateStr = new Date().toISOString().replace(/[:.]/g, '-'); //cb(null, `${dateStr}-${file.originalname}`); // 解決中文亂碼問題:將 latin1 轉回 utf-8 //const originalName = Buffer.from(file.originalname, 'latin1').toString('utf8'); //cb(null, `${file.originalname}`); // 1. 取得原始檔名與副檔名 const ext = path.extname(file.originalname); // 包含點,例如 .jpg const baseName = path.basename(file.originalname, ext); // 只有主檔名 // 2. 正則表達式:判斷是否為純英數、底線、橫線 // ^[a-zA-Z0-9_-]+$ 表示從頭到尾都是英數或底線橫線 const isAlphaNumeric = /^[a-zA-Z0-9_-]+$/.test(baseName); let finalFileName = ''; //const dateStr = new Date().toISOString().replace(/[:.]/g, '-'); const dateStr = new Date().toISOString().replace(/\D/g, ''); if (isAlphaNumeric) { // A. 如果是純英數,保持原樣,~~加上時間戳防止重複~~ //finalFileName = `${dateStr}-${file.originalname}`; finalFileName = `${file.originalname}`; } else { // B. 如果包含中文或其他字元 // 先修復 Multer 的編碼錯誤 (latin1 -> utf8) const decodedName = Buffer.from(file.originalname, 'latin1').toString('utf8'); // 為了系統穩定,硬碟端存儲建議改用「隨機英數」防止 URL 編碼問題 const uniqueSuffix = crypto.randomBytes(4).toString('hex'); finalFileName = `${dateStr}-${uniqueSuffix}${ext}`; // 將正確的中文名稱掛載,供 Controller 存入資料庫使用 file.decodedOriginalName = decodedName; } cb(null, finalFileName); } }); const upload = multer({ storage, limits: { fileSize: parseInt(process.env.MAX_FILE_SIZE) || 50 * 1024 * 1024 }, fileFilter: (req, file, cb) => { // 禁止上傳危險副檔名 const forbiddenExtensions = ['.exe', '.sh', '.php', '.js', '.bat']; const ext = path.extname(file.originalname).toLowerCase(); if (forbiddenExtensions.includes(ext)) { return cb(new Error('File type not allowed'), false); } cb(null, true); } }); module.exports = upload;