net core WebApi——文件分片下載

  • 2019 年 10 月 3 日
  • 筆記

前言

上一篇net core WebApi——文件分片上傳與跨域請求處理介紹完文件的上傳操作,本來是打算緊接著寫文件下載,中間讓形形色色的事給耽誤的,今天還是抽個空整理完文件這塊兒,然後就可以鼓搗別的東西了。

開始

這裡我們仍然使用基礎工程,需要下載的朋友請移步net core Webapi 總目錄,程式碼都是與部落格的進度基本同步的。
上傳的時候我們介紹過分片的思路,而下載也一樣,只是客戶端與服務端角色轉換下就好了。
後端

  1. 接收前端下載請求,校驗請求資訊,返迴文件基本資訊
  2. 根據前端請求文件片段進行下載流處理。

前端

  1. 向後端發起下載請求,獲取文件總片段數
  2. 根據片段數循環請求文件片段流進行下載(可單獨請求某一片段文件數據)

文件下載相對於上傳來說稍微簡潔點兒,如果不考慮伺服器壓力也可以一個a標籤解決下載問題,分片的意義就在於每次與服務端的交互減少流量,有些時候我們推薦拿空間換時間,但對於大流量來說還是慢慢來比較好,單次訪問量如果大再加上多並發怕是伺服器會受不了,所以有了一片片分步來循環訪問這個方法。

也是直接來看程式碼吧,我們在FileController創建幾個介面方法RequestDownloadFileFileDownload

        /// <summary>          /// 請求下載文件          /// </summary>          /// <param name="fileInfo">文件參數資訊[name]</param>          /// <returns></returns>          [HttpPost, Route("RequestDownload")]          public MessageEntity RequestDownloadFile([FromBody]Dictionary<string, object> fileInfo)          {            }            /// <summary>          /// 分段下載文件          /// </summary>          /// <param name="fileInfo">請求參數資訊[index,name]</param>          /// <returns></returns>          [HttpPost, Route("Download")]          public async Task<IActionResult> FileDownload([FromBody]Dictionary<string, object> fileInfo)          {            }

RequestDownloadFile

這裡說明下,與服務端的操作都要儘可能多的確認身份資訊(當然後續會有說這塊兒),文件的相關操作也一樣需要並且還要嚴格點兒,我這裡就是為了做示例演示所以只傳文件資訊即可。

        public MessageEntity RequestDownloadFile([FromBody]Dictionary<string, object> fileInfo)          {              MessageEntity message = new MessageEntity();              string fileName = string.Empty;              string fileExt = string.Empty;              if (fileInfo.ContainsKey("name"))              {                  fileName = fileInfo["name"].ToString();              }              if (fileInfo.ContainsKey("ext"))              {                  fileExt = fileInfo["ext"].ToString();              }              if (string.IsNullOrEmpty(fileName))              {                  message.Code = -1;                  message.Msg = "文件名不能為空";                  return message;              }              //獲取對應目錄下文件,如果有,獲取文件開始準備分段下載              string filePath = $".{AprilConfig.FilePath}{DateTime.Now.ToString("yyyy-MM-dd")}/{fileName}";              filePath = $"{filePath}{fileExt}";              FileStream fs = null;              try              {                  if (!System.IO.File.Exists(filePath))                  {                      //文件為空                      message.Code = -1;                      message.Msg = "文件尚未處理完";                      return message;                  }                  fs = new FileStream(filePath, FileMode.Open);                  if (fs.Length <= 0)                  {                      //文件為空                      message.Code = -1;                      message.Msg = "文件尚未處理完";                      return message;                  }                  int shardSize = 1 * 1024 * 1024;//一次1M                  RequestFileUploadEntity request = new RequestFileUploadEntity();                  request.fileext = fileExt;                  request.size = fs.Length;                  request.count = (int)(fs.Length / shardSize);                  if ((fs.Length % shardSize) > 0)                  {                      request.count += 1;                  }                  request.filedata = GetCryptoString(fs);                    message.Data = request;              }              catch (Exception ex)              {                  LogUtil.Debug($"讀取文件資訊失敗:{filePath},錯誤資訊:{ex.Message}");              }              finally              {                  if (fs != null)                  {                      fs.Close();                  }              }                return message;          }

FileDownload

        public async Task<IActionResult> FileDownload([FromBody]Dictionary<string, object> fileInfo)          {              //開始根據片段來下載              int index = 0;              if (fileInfo.ContainsKey("index"))              {                  int.TryParse(fileInfo["index"].ToString(), out index);              }              else              {                  return Ok(new { code = -1, msg = "缺少參數" });              }              string fileName = string.Empty;              string fileExt = string.Empty;              if (fileInfo.ContainsKey("name"))              {                  fileName = fileInfo["name"].ToString();              }              if (fileInfo.ContainsKey("ext"))              {                  fileExt = fileInfo["ext"].ToString();              }              if (string.IsNullOrEmpty(fileName))              {                  return Ok(new { code = -1, msg = "文件名不能為空" });              }              //獲取對應目錄下文件,如果有,獲取文件開始準備分段下載              string filePath = $".{AprilConfig.FilePath}{DateTime.Now.ToString("yyyy-MM-dd")}/{fileName}";              filePath = $"{filePath}{fileExt}";              if (!System.IO.File.Exists(filePath))              {                  return Ok(new { code = -1, msg = "文件尚未處理" });              }              using (var fs = new FileStream(filePath, FileMode.Open))              {                  if (fs.Length <= 0)                  {                      return Ok(new { code = -1, msg = "文件尚未處理" });                  }                  int shardSize = 1 * 1024 * 1024;//一次1M                  int count = (int)(fs.Length / shardSize);                  if ((fs.Length % shardSize) > 0)                  {                      count += 1;                  }                  if (index > count - 1)                  {                      return Ok(new { code = -1, msg = "無效的下標" });                  }                  fs.Seek(index * shardSize, SeekOrigin.Begin);                  if (index == count - 1)                  {                      //最後一片 = 總長 - (每次片段大小 * 已下載片段個數)                      shardSize = (int)(fs.Length - (shardSize * index));                  }                  byte[] datas = new byte[shardSize];                  await fs.ReadAsync(datas, 0, datas.Length);                  //fs.Close();                  return File(datas, "application/x-gzip");              }          }

看過上傳的朋友都清楚上傳是三步,請求上傳=>開始上傳=>合併,而下載只需要兩步,因為合併與否其實不那麼重要了,反正文件流都給客戶端了,那邊自己判斷需要重新下載還是下載部分片段都是他們自己的事了(服務端只管賣,東西有問題自己解決,多理想的狀態)。

測試

搞完之後重新生成,運行之後我們來測試下效果,測試之前不要忘了介面白名單(做過登錄相關的驗證操作的忽略這點)。
測試

這裡提示error是因為解析錯誤,實際請求下載測試是正常的,如果有異常問題可以與我聯繫。

小結

文件相關的上傳下載以及常規資訊的操作可以告一段落,至於下一步鼓搗點兒啥也還沒想好,本來還在看著linux相關的操作做發布部署的鋪墊,看最近總體的進度吧,總之,學如逆水行舟,如果不想溺水,就握好你的漿(當然有些人不用漿那就算了,告辭)。