Я пытаюсь создать файл Excel в моем ASP. NET бэкэнде Web API и отправить его в мой 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
});
}
В бэкэнде файл Excel создается внутри QueryResultsExport.cs (EPPlus - версия 4.1.0):
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 байт.
Вот изображение заголовков ответа:
Вот изображение Console.log (результат):
Я пробовал разные вещи, такие как изменение responseType, тип, передаваемый в конструктор BLOB-объектов, заголовок ContentType, заголовок Accept в запрос, как поток памяти добавляется в ответ, и т. д. c, но мне не повезло заставить его работать: результат всегда одинаков; поврежденный файл Excel размером 5635 байт вместо ожидаемых 3442 байт. Любая помощь будет очень высоко ценится; спасибо!
редактировать: я заметил в фрагментах кода, которые я вставил, у меня есть
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;
Но ничего из этого не имеет никакого значения.