Застрял генерация + отправка файла .xlsx из ASP. NET бэкэнда веб-API в Vue. js внешний интерфейс - PullRequest
0 голосов
/ 04 марта 2020

Я пытаюсь создать файл 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 байт.

Вот изображение заголовков ответа: enter image description here

Вот изображение Console.log (результат): enter image description here

Я пробовал разные вещи, такие как изменение 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;

Но ничего из этого не имеет никакого значения.

1 Ответ

0 голосов
/ 06 марта 2020

Мне удалось заставить его работать, отправив файл Excel как StringContent вместо ByteArrayContent, как показано здесь: Как загрузить ByteArrayContent для HttpResponseMessage в виде 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();
})

Назначение responseType в запросе API не представляется необходимым. Также не устанавливается Content.Headers.ContentDisposition или Content.Headers.ContentType.

...