前端表單 (Razor / .cshtml)
@using (Html.BeginForm("FileUpload", "Book", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<input type="file" id="imageUpload" name="imageUpload" accept="image/*" />
<input type="submit" value="上傳圖片" />
@* 或使用 AJAX 上傳 (見下方) *@
}
Controller 端處理
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase imageUpload)
{
// 1. 基本驗證
if (imageUpload == null || imageUpload.ContentLength == 0)
{
ModelState.AddModelError("", "請選擇檔案");
return View();
}
// 2. 檔案類型驗證 (雙重保險)
string[] allowedTypes = { "image/jpeg", "image/png", "image/gif", "image/bmp" };
if (!allowedTypes.Contains(imageUpload.ContentType))
{
ModelState.AddModelError("", "僅支援 JPG/PNG/GIF/BMP 格式");
return View();
}
// 3. 檔案大小限制 (例:4MB)
if (imageUpload.ContentLength > 4 * 1024 * 1024)
{
ModelState.AddModelError("", "檔案大小不可超過 4MB");
return View();
}
// 4. 取得原始檔名並處理路徑字元
string fileName = Path.GetFileName(imageUpload.FileName); // 自動移除路徑資訊
// 5. 產生唯一檔名避免衝突 (GUID + 原始副檔名)
string ext = Path.GetExtension(fileName);
string uniqueName = $"{Guid.NewGuid()}{ext}";
// 6. 確保儲存目錄存在
string saveDir = Server.MapPath("~/Content/Uploads/Images/");
if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
string savePath = Path.Combine(saveDir, uniqueName);
// 7. 儲存檔案
imageUpload.SaveAs(savePath);
// 8. 存入資料庫 (相對路徑)
string relativePath = $"/Content/Uploads/Images/{uniqueName}";
// db.BookPhotos.Add(new BookPhoto { Path = relativePath });
// db.SaveChanges();
TempData["Success"] = "上傳成功!";
return RedirectToAction("Index");
}
AJAX 非同步上傳 (jQuery + FormData)
// 前端 JS
$('#imageUpload').on('change', function() {
var file = this.files[0];
if (!file) return;
var formData = new FormData();
formData.append('imageUpload', file);
$.ajax({
url: '@Url.Action("FileUpload", "Book")',
type: 'POST',
data: formData,
processData: false, // 必須 false
contentType: false, // 必須 false,讓瀏覽器自動設定 multipart boundary
success: function(result) {
if (result.success) {
alert('上傳成功:' + result.path);
} else {
alert('失敗:' + result.message);
}
},
error: function() { alert('網路錯誤'); }
});
});
// Controller 回傳 JSON
[HttpPost]
public JsonResult FileUpload(HttpPostedFileBase imageUpload)
{
// ... 驗證與儲存邏輯同前 ...
return Json(new { success = true, path = relativePath });
}
安全性關鍵點總結
| 風險 | 防護措施 |
| 惡意腳本上傳 | 驗證 ContentType、副檔名、檔案標頭 (Magic Bytes) |
| 路徑遍歷攻擊 | 使用 Path.GetFileName、不信任用戶檔名、重新命名 |
| 大檔案 DoS | 設定 maxRequestLength (web.config) 與 requestLimits (IIS) |
| 直接存取上傳檔 | 儲存於非公開目錄,透過 Controller 串流輸出或簽名 URL |
web.config 請求大小限制
<system.web>
<!-- 最大 10MB,預設 4MB -->
<httpRuntime maxRequestLength="10240" />
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="10485760" /> <!-- 10MB in bytes -->
</requestFiltering>
</security>
</system.webServer>