方法一
使用場景
針對后端的get
請求
具體實現
-
<a href= "后端文件下載接口地址" >下載文件</a>
-
復制代碼
直接用個<a>
標簽來接受后端的文件流
方法二
使用場景
針對后端的post
請求 利用原生的XMLHttpRequest
方法實現
具體實現
-
function request () {
-
const req = new XMLHttpRequest();
-
req.open( 'POST', '<接口地址>', true);
-
req.responseType = 'blob';
-
req.setRequestHeader( 'Content-Type', 'application/json');
-
req.onload = function() {
-
const data = req.response;
-
const blob = new Blob([data]);
-
const blobUrl = window.URL.createObjectURL(blob);
-
download(blobUrl) ;
-
};
-
req.send( '<請求參數:json字符串>');
-
};
-
-
function download(blobUrl) {
-
const a = document.createElement( 'a');
-
a.download = '<文件名>';
-
a.href = blobUrl;
-
a.click();
-
}
-
-
request();
-
復制代碼
方法三
使用場景
針對后端的post
請求 利用原生的fetch
方法實現
具體實現
-
function request() {
-
fetch( '<接口地址>', {
-
method: 'POST',
-
headers: {
-
'Content-Type': 'application/json',
-
},
-
body: '<請求參數:json字符串>',
-
})
-
. then(res => res.blob())
-
. then(data => {
-
let blobUrl = window.URL.createObjectURL(data);
-
download(blobUrl);
-
});
-
}
-
-
function download(blobUrl) {
-
const a = document.createElement( 'a');
-
a.download = '<文件名>';
-
a.href = blobUrl;
-
a.click();
-
}
-
-
request();
-
復制代碼
總結
- 如果后端提供的下載接口是
get
類型,可以直接使用方法一,簡單又便捷;當然如果想使用方法二、三也是可以的,不過感覺有點舍近求遠了。 - 如果后端提供的下載接口是
post
類型,就必須要用方法二或者方法三了。
方法二和方法三怎么取舍?
- 當你的項目里的接口請求全是基於
XMLHttpRequest
實現的,這時方法二就更加適合,只要基於你原來項目中的接口請求工具類加以擴展就行了。 - 當你的項目里的接口請求全是基於
fetch
實現的,這時方法三就更加適合,比如我現在的做的一個項目就是基於ant design pro
的后台管理系統,它里面的請求類就是基於fetch
的,所以我就直接用的方法三,只要在它的request.js
文件中稍作修改就行。 - 我這里討論的是兩種原生的請求方式,如果你項目中引用了第三方請求包來發送請求,比如axios之類的,那就要另當別論了。
以下是個人項目中 利用上述方法二改成針對后端的get請求下載
效果圖
前端:
html:
js:
/// <summary>
/// DownloadBigFile用於下載大文件,循環讀取大文件的內容到服務器內存,然后發送給客戶端瀏覽器
/// </summary>
[HttpGet("buckets/file/download")]
public IActionResult DownloadBigFile([Required] string bucketName, [Required] string fileName)
{
var s3Client = GetAS3Client;
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = fileName
};
int bufferSize = 10240000;//這就是ASP.NET Core循環讀取下載文件的緩存大小,這里我們設置為了1024字節,也就是說ASP.NET Core每次會從下載文件中讀取1024字節的內容到服務器內存中,然后發送到客戶端瀏覽器,這樣避免了一次將整個下載文件都加載到服務器內存中,導致服務器崩潰
var contentDisposition = "attachment;" + "filename=" + HttpUtility.UrlEncode(fileName);//在Response的Header中設置下載文件的文件名,這樣客戶端瀏覽器才能正確顯示下載的文件名,注意這里要用HttpUtility.UrlEncode編碼文件名,否則有些瀏覽器可能會顯示亂碼文件名
Response.Headers.Add("Content-Disposition", new string[] { contentDisposition });
//使用FileStream開始循環讀取要下載文件的內容
using (GetObjectResponse response = s3Client.GetObjectAsync(request).Result)
using (Stream fs = response.ResponseStream)
{
Response.ContentType = response.Headers["Content-Type"];
using (Response.Body)//調用Response.Body.Dispose()並不會關閉客戶端瀏覽器到ASP.NET Core服務器的連接,之后還可以繼續往Response.Body中寫入數據
{
long contentLength = fs.Length;//獲取下載文件的大小
Response.ContentLength = contentLength;//在Response的Header中設置下載文件的大小,這樣客戶端瀏覽器才能正確顯示下載的進度
byte[] buffer;
long hasRead = 0;//變量hasRead用於記錄已經發送了多少字節的數據到客戶端瀏覽器
//如果hasRead小於contentLength,說明下載文件還沒讀取完畢,繼續循環讀取下載文件的內容,並發送到客戶端瀏覽器
while (hasRead < contentLength)
{
//HttpContext.RequestAborted.IsCancellationRequested可用於檢測客戶端瀏覽器和ASP.NET Core服務器之間的連接狀態,如果HttpContext.RequestAborted.IsCancellationRequested返回true,說明客戶端瀏覽器中斷了連接
if (HttpContext.RequestAborted.IsCancellationRequested)
{
//如果客戶端瀏覽器中斷了到ASP.NET Core服務器的連接,這里應該立刻break,取消下載文件的讀取和發送,避免服務器耗費資源
break;
}
buffer = new byte[bufferSize];
int currentRead = fs.ReadAsync(buffer, 0, bufferSize).Result;//從下載文件中讀取bufferSize(1024字節)大小的內容到服務器內存中
Response.Body.Write(buffer, 0, currentRead);//發送讀取的內容數據到客戶端瀏覽器
Response.Body.Flush();//注意每次Write后,要及時調用Flush方法,及時釋放服務器內存空間
hasRead += currentRead;//更新已經發送到客戶端瀏覽器的字節數
}
}
}
}
catch (AmazonS3Exception e)
{
// If bucket or object does not exist
Console.WriteLine("Error encountered ***. Message:'{0}' when reading object", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Unknown encountered on server. Message:'{0}' when reading object", e.Message);
}
return new EmptyResult();
}