iT邦幫忙

1

# 如何實作上傳檔案功能

如何實作上傳檔案功能

小明問說他要製作在網頁中上傳檔案, 但不知道要怎麼做?

提供使用者將檔案上傳到伺服器的能力時, 請務必小心. 因為攻擊者可能會嘗試

  • 執行阻斷服務的攻擊
  • 上傳病毒或惡意程式碼
  • 以其他方式危害網路和伺服器

降低成功攻擊的可能性的安全性方法如下

  • 將檔案上傳到專用的檔案上傳區域, 最好是在非系統磁碟機上. 並停用檔案上傳位置的 execute 許可權.
  • 請勿將上傳的檔案保存在與應用程式相同的目錄樹狀結構中.
  • 使用應用程式所決定的安全檔案名. 請勿使用使用者所提供的檔案名稱或不受信任檔案名稱.
  • 檢查上傳檔案的大小, 設定最大大小限制以防止大量上傳.

當涉及到上傳較大的檔案時, 一般人總是認為直接修改Web 設定

maxRequestLength 和 maxAllowedContentLength 設定

這絕對不是正確的做法, 因為你總不可能無限制地加大Request 上傳大小,
加大上傳大小更容易受到阻斷服務的攻擊.

而且一次性上傳大檔案會遇到很多挑戰, 例如UI 回應能力, 上傳進度, 客戶端必須再次上載等等.
在這種情況下, 最好分塊上傳大檔案, 它具有很多優點, 例如

  • 您可以顯示上傳進度
  • 如果上傳過程失敗, 則可以繼續上傳檔案的其餘部分
  • 您還可以通過添加一些額外的程式碼來實現暫停/恢復上傳功能

在這裡我將展示如何以小塊形式上傳檔案

首先在網頁中輸入以下內容

<input type="file" accept=".pdf,.png" onChange="fileUpload">

接著準備Javascript 程式碼

<script>
function fileUpload(sender) {
   var fileList = sender.currentTarget.files;
   var file = fileList[0];
   chunky(file, 0);
}

function chunky(file, chunkIndex) {
   let chunkSize = 1024 * 4;
   let totalSize = file.size;

   let totalChunkSize = Math.floor((totalSize + chunkSize - 1) / chunkSize);
   if( chunkIndex >= totalChunkSize ) {
      return;
   }

   let start = chunkIndex * chunkSize;
   let blob = file.slice(start, start + chunkSize);

   var dataForm = new DataForm();
   dataForm.append("fileUpload", blob, file.name);
   dataForm.append("chunkIndex", chunkIndex);
   dataForm.append("totalSize", totalSize);

   $.ajax({
      url: "/Home/UploadFileChunk",
      type: "POST",
      processData: false,
      contentType: false
      data: dataForm
   }).done(function(r){
      chunky(file, chunkIndex+1);
   });
}
</script>

在客戶端將每個檔案切成小塊,每一塊大小在 "chunkSize" 中指定, 您可以更改此大小.
Javascript 上傳每個塊, 然後等待其完成, 然後再開始上傳下一個塊.

以下是伺服器端的實作程式碼

public class UploadRequest {
   public HttpPostedFileBase File { get; set }
   public int ChunkIndex { get; set; }
   public int TotalSize { get; set; }
}

public ActionResult UploadFileChunk(UploadRequest req) {
   var inputStream = req.File.InputStream;
   using(var fs=new FileStream("D:/tmp/customer.png", FileMode.Append, FileAccess.Write))
   {
      var buffer = new byte[1024];
      var readSize = inputStream.Read(buffer, 0, 1024);
      while (readSize > 0)
      {
        fs.Write(buffer, 0, readSize);
        readSize = inputStream.Read(buffer, 0, 1024);
      }
      fs.Flush();
      fs.Close();
   }
   return Json("OK");
}

這只是個示範示意程式, 不是正式完整的程式碼.

在伺服器端, 我們都接收到前端傳送過來是一個單獨的檔案塊, 將其保存到臨時資料夾中,
每次收到檔案塊, 需要將它們合併回原始檔案中.
所以我們需要輸入三個參數(檔案區塊內容File, 塊索引ChunkIndex, 原始檔案的總共大小TotalSize)

你必須檢查這三個參數內容是否合法.

最後我們需要注意以下幾點

  • 當 ChunkIndex == 0 的時候, 就建立全新的暫存檔案
  • 當 ChunkIndex == 最後檔案區塊索引, 就相當於上傳結束
  • 檢查 ChunkIndex 索引值, File 大小是否在合理的範圍內

就是這樣-祝您上傳愉快!


尚未有邦友留言

立即登入留言