[英]Stuck generating + sending .xlsx file from ASP.NET Web API backend to Vue.js frontend
我正在嘗試在我的 ASP.NET Web API 后端生成一個 excel 文件,並將其發送到我的 Vue.js 前端進行下載。
API 請求是通過調用 getQueryResults 發出的:
const apiClient = axios.create({
baseURL: process.env.NODE_ENV == "production" ? PROD_URL : TEST_URL,
withCredentials: true, // This is the default
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});
getQueryResults(table, filters, selected, clientId, fundId, periodId, pageNumber, pageSize, exportExcel){
return apiClient.post(`queryResults`, {
responseType: 'blob',
table,
filters,
selected,
clientId,
fundId,
periodId,
pageNumber,
pageSize,
exportExcel
});
}
在后端,在 QueryResultsExport.cs(EPPlus 版本為 4.1.0)中生成了 excel 文件:
public static MemoryStream exportToExcel(IQueryable<object> itemsToExport)
{
if (itemsToExport.Count() == 0)
return null;
ExcelPackage excel = new ExcelPackage();
ExcelWorksheet workSheet = excel.Workbook.Worksheets.Add("Query Results");
DataTable dataTable = toDataTable(itemsToExport.ToList());
workSheet.Cells["A1"].LoadFromDataTable(dataTable, true);
/*
string path = @"C:\Users\asdfq321\Downloads\test11.xlsx";
Stream stream = File.Create(path);
excel.SaveAs(stream);
stream.Close();
return null;
*/
MemoryStream ms = new MemoryStream(excel.GetAsByteArray());
excel.Dispose();
return ms;
}
您可以看到我注釋掉了一些將 excel 文件在本地保存為 .xlsx 文件的代碼,而不是返回內存流。 我這樣做是為了確保問題不在 excel 一代中。 本地保存在服務器上的文件似乎是正確的,它的大小為 3442 字節(稍后會相關)。
接下來,將內存流發送回客戶端:
[HttpPost]
[Route("queryResults")]
public HttpResponseMessage GetQueryResults([FromBody]Dictionary<string, object> queryParams)
{
// continued from above...
MemoryStream ms = QueryResultsExport.exportToExcel(queryItems);
HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new StreamContent(ms);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "asdfq.xlsx";
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
return httpResponseMessage;
}
最后,在 Vue 前端,對響應進行處理:
.then(result => {
var myBlob = new Blob([result.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
const url = window.URL.createObjectURL(myBlob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'asdfq.xlsx'); //or any other extension
document.body.appendChild(link);
link.click();
})
結果是出現一個彈出框,提示我下載一個excel文件。 但是,無法讀取該文件,並且在打開該文件時顯示消息“我們發現 asdfq.xlsx 中的某些內容存在問題。您希望我們盡可能多地嘗試恢復嗎?如果您信任此消息的來源工作簿,單擊是。” 當我單擊是時,我得到“Excel 無法打開文件 asdfq.xlsx,因為文件格式或文件擴展名無效。請確認文件沒有損壞,並且文件擴展名與文件格式匹配。”
當我查看瀏覽器開發工具網絡選項卡中的響應時,我看到響應的 Content-Length: 3442,這似乎是正確的:這與我之前成功保存在服務器上的 excel 文件相匹配。 但是,最終下載的 excel 文件的大小為 5635 字節。
我嘗試了各種事情,例如更改 responseType、傳遞給 blob 構造函數的類型、標頭 ContentType、請求中的 Accept 標頭、如何將內存流添加到響應等,但我沒有運氣得到這個工作:結果總是一樣的; 大小為 5635 字節而不是預期的 3442 字節的損壞的 excel 文件。 任何幫助將不勝感激; 謝謝!
編輯:我在粘貼的代碼片段中注意到,我有
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
在后端,但在前端我有
var myBlob = new Blob([result.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
我確實嘗試將它們都設置為“application/octet-stream”,或都設置為“application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”,但這沒有區別。
我還嘗試了不同的生成響應的方法:
//attempt 1
MemoryStream ms = QueryResultsExport.exportToExcel(queryItems);
HttpResponseMessage httpResponseMessage = Request.CreateResponse(HttpStatusCode.OK);
httpResponseMessage.Content = new StreamContent(ms);
httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
httpResponseMessage.Content.Headers.ContentDisposition.FileName = "asdfq.xlsx";
httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
// ms.Close();
return httpResponseMessage;
//attempt 2
MemoryStream ms = QueryResultsExport.exportToExcel(queryItems);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.ToArray())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "asdfq.xlsx";
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
return result;
// atempt 3
MemoryStream ms = QueryResultsExport.exportToExcel(queryItems);
ms.Position = 0;
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.GetBuffer())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment"){FileName = "export.xlsx"};
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
result.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
result.Content.Headers.Add("content-length", ms.Length.ToString());
return result;
最后,在QueryResultsExport.exportToExcel中,我也試過了
var ms = new MemoryStream();
excel.SaveAs(ms);
return ms;
但這一切都沒有任何區別。
我能夠通過將 excel 文件作為 StringContent 而不是 ByteArrayContent 發送來使其工作,如下所示: How to download ByteArrayContent of HttpResponseMessage as zip
MemoryStream ms = QueryResultsExport.exportToExcel(queryItems);
string base64String = System.Convert.ToBase64String(ms.ToArray(), 0, ms.ToArray().Length);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(base64String)
};
return result;
.then(result => {
const link = document.createElement('a');
link.href = 'data:application/octet-stream;charset=utf-8;base64,' + result.data;
link.target = '_blank';
link.download = 'asdfq.xlsx';
link.click();
})
在 api 請求中分配 responseType 似乎沒有必要。 設置 Content.Headers.ContentDisposition 或 Content.Headers.ContentType 也不行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.