功能需求
- 支援單檔上傳 (HTTP POST)
- 限制副檔名:.png、.gif、.jpg、.jpeg
- 限制檔案大小:不超過 500 KB
- 自動產生唯一檔名避免衝突:時間戳 + 隨機數 + 原副檔名
- 自動建立上傳目錄
- 回傳 JSON 格式結果:成功回傳檔名,失敗回傳錯誤訊息
控制器實作
[HttpPost]
public async Task<IActionResult> UploadImage(IFormFile uploadFile)
{
// 1. 基本防禦性檢查
if (uploadFile == null || uploadFile.Length == 0)
return Json(new { success = false, message = "未收到檔案" });
// 2. 副檔名驗證 (不區分大小寫)
string fileExt = Path.GetExtension(uploadFile.FileName).ToLowerInvariant();
string[] allowedExts = { ".png", ".gif", ".jpg", ".jpeg" };
if (!allowedExts.Contains(fileExt))
return Json(new { success = false, message = "圖片類型僅限 gif、png、jpg、jpeg" });
// 3. 檔案大小驗證 (500 KB = 512,000 bytes)
const long maxSize = 500 * 1024;
if (uploadFile.Length > maxSize)
return Json(new { success = false, message = "圖片大小不能超過 500 KB" });
// 4. 產生唯一檔名:yyyyMMddHHmmss + 6位隨機數 + 副檔名
string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
string randomPart = RandomNumberGenerator.GetInt32(100000, 999999).ToString();
string saveFileName = $"{timestamp}{randomPart}{fileExt}";
// 5. 確保目錄存在
string uploadFolder = Path.Combine(_hostEnvironment.WebRootPath, "UploadImages");
if (!Directory.Exists(uploadFolder))
Directory.CreateDirectory(uploadFolder);
string savePath = Path.Combine(uploadFolder, saveFileName);
try
{
// 6. 非同步儲存檔案
using (var stream = new FileStream(savePath, FileMode.Create))
{
await uploadFile.CopyToAsync(stream);
}
// 7. 回傳成功結果 (可擴充回傳相對路徑供前端預覽)
return Json(new { success = true, fileName = saveFileName, url = $"/UploadImages/{saveFileName}" });
}
catch (Exception ex)
{
_logger.LogError(ex, "圖片上傳失敗");
return Json(new { success = false, message = "伺服器內部錯誤,請稍後再試" });
}
}
前端呼叫範例 (jQuery AJAX)
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="uploadFile" id="fileInput" accept="image/*" />
<button type="button" id="btnUpload">上傳</button>
</form>
<div id="preview" style="margin-top:10px;"></div>
<script>
$('#btnUpload').click(function() {
var file = $('#fileInput')[0].files[0];
if (!file) return alert('請選擇檔案');
var formData = new FormData();
formData.append('uploadFile', file);
$.ajax({
url: '/Home/UploadImage',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function(res) {
if (res.success) {
$('#preview').html(`<img src="${res.url}" style="max-width:300px;" /><br>檔名: ${res.fileName}`);
} else {
alert('上傳失敗:' + res.message);
}
},
error: function() { alert('網路錯誤'); }
});
});
</script>
安全性強化建議 (生產環境必做)
🛡️ 關鍵防護措施
- MIME Type 二次驗證:不要只信任副檔名,讀取檔案 Header (Magic Bytes) 確認真實格式
// 簡易 PNG/JPEG/GIF Magic Bytes 檢查
byte[] header = new byte[8];
await uploadFile.OpenReadStream().ReadAsync(header, 0, 8);
bool isValid = header.Take(4).SequenceEqual(new byte[]{0x89,0x50,0x4E,0x47}) || // PNG
header.Take(2).SequenceEqual(new byte[]{0xFF,0xD8}) || // JPEG
header.Take(6).SequenceEqual(new byte[]{0x47,0x49,0x46,0x38,0x37,0x61}) || // GIF87a
header.Take(6).SequenceEqual(new byte[]{0x47,0x49,0x46,0x38,0x39,0x61}); // GIF89a
- 禁用執行權限:上傳目錄在 IIS 設定「執行權限 = 無」或放在 WebRoot 外部
- 檔案重命名:絕對不要使用原始檔名,防止路徑遍歷攻擊
- 病毒掃描整合:整合 ClamAV 或商業反病毒 API 掃描上傳檔案
- 大小限制在伺服器層:
web.config 設定 <requestLimits maxAllowedContentLength="524288" /> (500KB)
- CSRF 保護:加入 AntiForgeryToken 驗證
擴充方向
- 支援多檔上傳 (
IFormFileCollection)
- 圖片壓縮/縮圖生成 (引用
ImageSharp 或 SkiaSharp)
- 雲端儲存整合 (Azure Blob、AWS S3、阿里云 OSS)
- 資料庫記錄上傳日誌 (檔名、大小、上傳者、IP、時間)
- 臨時檔案自動清理排程