tw1742
tw1742

Reputation: 1484

Axios Excel file download using POST results in corrupted file

I was using Axios to download a file provided by a GET endpoint previously. The endpoint has changed and is now a POST, however the parameters are not required. I'm updating the original download method, but am getting a corrupted file returned.

downloadTemplate() {
        axios.post(DOWNLOAD_TEMPLATE_URL,
            {
                responseType: 'blob',
                headers: {
                    'Content-Disposition': "attachment; filename=template.xlsx",
                    'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                }
            })
            .then((response) => {
                const url = window.URL.createObjectURL(new Blob([response.data]));
                const link = document.createElement('a');
                link.href = url;
                link.setAttribute('download', 'template.xlsx');
                document.body.appendChild(link);
                link.click();
            })
            .catch((error) => console.log(error));
    }

I'm not sure if the issue is with the responseType, headers, or how the response is handled or all of the above. I've tried various options with no luck so far. Any suggestions would be greatly appreciated!

I have been able to download the file using Postman so I know the file served by the endpoint is fine. I just can't sort out the params to do this in my React code.

Upvotes: 19

Views: 26684

Answers (2)

Md. Nazrul Islam
Md. Nazrul Islam

Reputation: 3017

We can use the following code to export Excel files from the POST method. May it help someone and save time.

For API use .Net Core 2.2 and the method is below.

Note: When we create a FileStreamResult, Content-Disposition header for the response will contain the filename and the stream will come as an attachment.

Add the "Content-Disposition" to Cors at Startup file,

 app.UseCors(b => b.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials().WithExposedHeaders("Content-Disposition"));

I am using the EPplus package for generating the Excel file.

using OfficeOpenXml;
using OfficeOpenXml.Style;

    public static MemoryStream InvoiceToExcel(List<InvoiceSearchDto> invoices)
            {
                var listOfFieldNames = typeof(InvoiceSearchDto).GetProperties().Select(f => f.Name).ToList();                   
                int cellCounter = 1, recordIndex = 2;

                var ms = new MemoryStream();

                using (ExcelPackage package = new ExcelPackage(ms))
                {
                    ExcelWorksheet worksheet;

                    worksheet = package.Workbook.Worksheets.Add("New HGS");

                    // Setting the properties of the first row 
                    worksheet.Row(1).Height = 20;
                    worksheet.Row(1).Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
                    worksheet.Row(1).Style.Font.Bold = true;

                    // Header of the Excel sheet 
                    foreach (string header in listOfFieldNames)
                    {
                        worksheet.Cells[1, cellCounter++].Value = header;
                    }

                    // Inserting the article data into excel 
                    // sheet by using the for each loop 
                    // As we have values to the first row  
                    // we will start with second row 
                    foreach (InvoiceSearchDto invoice in invoices)
                    {
                        worksheet.Cells[recordIndex, 1].Value = invoice.CompanyName;
                        worksheet.Cells[recordIndex, 2].Value = invoice.CustomerNo;
                        worksheet.Cells[recordIndex, 3].Value = invoice.DocumentNumber;
                        worksheet.Cells[recordIndex, 4].Value = invoice.BillingPeriodStartDate.ToString("YYYY-MM-DD");
                        worksheet.Cells[recordIndex, 5].Value = invoice.BillingPeriodEndDate.ToString("YYYY-MM-DD");
                        worksheet.Cells[recordIndex, 6].Value = invoice.DateOfInvoice.ToString("YYYY-MM-DD");
                        worksheet.Cells[recordIndex, 7].Value = invoice.ExpirationDate.ToString("YYYY-MM-DD");
                        worksheet.Cells[recordIndex, 8].Value = invoice.Amount;
                        worksheet.Cells[recordIndex, 9].Value = invoice.InvoiceStatusText;

                        recordIndex++;
                    }

                    // By default, the column width is not  
                    // set to auto fit for the content 
                    // of the range, so we are using 
                    // AutoFit() method here.  
                    worksheet.Column(1).AutoFit();
                    worksheet.Column(2).AutoFit();
                    worksheet.Column(3).AutoFit();
                    worksheet.Column(4).AutoFit();
                    worksheet.Column(5).AutoFit();
                    worksheet.Column(6).AutoFit();
                    worksheet.Column(7).AutoFit();
                    worksheet.Column(8).AutoFit();
                    worksheet.Column(9).AutoFit();
                    package.Save();
                }

                ms.Position = 0;
                return ms;
            }

The Action Method code is as below

[HttpPost]
        [Route("[action]")]
        public IActionResult GetInvoiceWithExcel([FromBody]SearchInvoice searchInvoice)
        {
            try
            {
                if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateFrom))
                {
                    searchInvoice.DateFrom = Convert.ToDateTime(searchInvoice.InvoiceDateFrom);
                }
                if (!string.IsNullOrEmpty(searchInvoice.InvoiceDateTo))
                {
                    searchInvoice.DateTo = Convert.ToDateTime(searchInvoice.InvoiceDateTo);
                }

                var invoices = invoiceBatchService.GetAllForExcel(searchInvoice.PagingParams, searchInvoice, searchInvoice.FilterObject);

                if (invoices != null)
                {
                    MemoryStream invoiceStream = ExcelConverter.InvoiceToExcel(invoices);

                    var contentType = "application/octet-stream";
                    var fileName = "Invoice.xlsx";

                    return File(invoiceStream, contentType, fileName);
                }
                else
                {
                    ResponseModel.Notification = Utility.CreateNotification("Not Found Anything", Enums.NotificationType.Warning);
                    return NotFound(ResponseModel);
                }
            }
            catch (Exception ex)
            {
                NLogger.LogError(ex, "Get Invoice With Excel");
                ResponseModel.Notification = Utility.CreateNotification(Helpers.ExceptionMessage(ex), Enums.NotificationType.Error);
                return StatusCode(500, ResponseModel);
            }
        }

Finally the React and axois code as below.

the Service code:

 return http.post(
      API_BASE_URL + "/Invoice/GetInvoiceWithExcel",
      searchInvoice,
      {
        headers: getHeaders(), // for token and others
        responseType: 'blob' // **don't forget to add this**
      }
    );
  };

And the Action method Code is below. Here I use the "file-saver" package to download the file.

import { saveAs } from 'file-saver';

export const getInvoiceWithExcel = invoiceInfo => {
  return dispatch => {
    dispatch({
      type: LOADING_ON
    });

    InvoiceService.getInvoiceWithExcel(invoiceInfo)
      .then(res => {

        console.log(res);

        let filename = res.headers['content-disposition']
          .split(';')
          .find((n) => n.includes('filename='))
          .replace('filename=', '')
          .trim();
        let url = window.URL
          .createObjectURL(new Blob([res.data]));
        saveAs(url, filename);


        dispatch({
          type: GET_INVOICE_EXCEL_SUCCESS,
          payload: ""
        });
        dispatch({
          type: LOADING_OFF
        });
        dispatch({
          type: ON_NOTIFY,
          payload: {
            ...res.data.notification
          }
        });
      })
      .catch(err => {
        dispatch({
          type: GET_INVOICE_EXCEL_FAILED
        });
        dispatch({
          type: LOADING_OFF
        });
        dispatch({
          type: ON_NOTIFY,
          payload: {
            ...Utility.errorResponseProcess(err.response)
          }
        });
      });
  };
};

Upvotes: 0

tw1742
tw1742

Reputation: 1484

Finally got it working! The post syntax in the code block for the question was not correct and also changed the responseType to "arraybuffer".

Working example below:

downloadTemplate() {
    axios.post(DOWNLOAD_TEMPLATE_URL, null,
        {
            headers:
            {
                'Content-Disposition': "attachment; filename=template.xlsx",
                'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            },
            responseType: 'arraybuffer',
        }
    ).then((response) => {
        const url = window.URL.createObjectURL(new Blob([response.data]));
        const link = document.createElement('a');
        link.href = url;
        link.setAttribute('download', 'template.xlsx');
        document.body.appendChild(link);
        link.click();
    })
        .catch((error) => console.log(error));
}

Upvotes: 42

Related Questions