70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
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; |