Файл Excel, созданный в приложении Springboot, не открывается после загрузки с AWS Ec2.

Вопрос или проблема

Я использую следующий код в своем контроллере

@GetMapping(value = "/export", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Resource> export(HttpServletResponse httpResponse
    ) {
        try {

            List<MyOutPut> data = mydata();
            List<String> fieldsToInclude = List.of("id", "Name");
            ExportRequest<MyOutPut, ExcelDesignOption> request = new ExportRequest<>(
                    ExportRequest.ExportFormat.EXCELSX, data, fieldsToInclude, MyOutPut.class, "export_data", new ExcelDesignOption()
            );

            System.out.println("Данные получены сейчас");
            ExportUseCase<MyOutPut, ExcelDesignOption> exportUseCase = new ExportUseCase<>();
            ExportResponse response = exportUseCase.execute(request);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentDispositionFormData("attachment", response.getFileName());
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//            httpResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            System.out.println("Данные получены, теперь они готовы к отображению от поставщика");
            // Вернуть файл как ответ в виде массива байтов

//            org.apache.commons.io.IOUtils.copy(response.getInputStream(), httpResponse.getOutputStream());
//            httpResponse.flushBuffer();
            InputStreamResource file = new InputStreamResource(response.getInputStream());

            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "export_data.xlsx")
                    .header(HttpHeaders.CONTENT_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
                    .contentType(MediaType.parseMediaType("application/vnd.ms-excel")).body(file);
        } catch (Exception e) {
            log.error("Операция поиска не удалась ", e);
            throw new RuntimeException("Ошибка экспорта Excel файла", e);
        }
    }

Это работает локально на моем компьютере с Windows, но когда я разворачиваю это на AWS Ec2 и интегрирую API из Angular UI, файл загружается, но не открывается.

Вот как в Angular интегрируется экспорт файла с сервера

.downloadExcel(this.id).subscribe({
      next: ({ blob, filename }) =>
        this.myService.handleFileDownload(blob, filename),
      error: (err) => console.error('Ошибка загрузки файла', err)
    });

return this.http.get(url, {
      observe: 'response',
      responseType: 'blob'
    }).pipe(
      map(response => {
        const contentDisposition = response.headers.get('Content-Disposition');
        const filename = contentDisposition
          ? contentDisposition.split('filename="')[1].replace(/"/g, "")
          : 'Список зарегистрированных поставщиков.xlsx';

        return { blob: response.body as Blob, filename };
      })
    );

Структура файла
введите описание изображения здесь

Ошибка
введите описание изображения здесь

Убедитесь:

  • Тип содержимого в ответе и на клиенте согласован и измените его на application/vnd.openxmlformats-officedocument.spreadsheetml.sheet для .xlsx и application/vnd.ms-excel для .xls, есть несоответствие в заголовке и типе содержимого в java коде.
  • Кодировка правильная headers.setContentDispositionFormData("attachment", URLEncoder.encode(response.getFileName(), StandardCharsets.UTF_8.toString()));

Ответ или решение

Для решения проблемы с загрузкой файла Excel, который не открывается после скачивания с вашего Spring Boot приложения, размещенного на AWS EC2, необходимо учесть несколько аспектов, которые могут влиять на формирование и передачу файла.

Возможные причины проблемы

  1. Несоответствующий Content-Type: В заголовках ответа Content-Type должен четко соответствовать типу файла. Для Excel файлов формата .xlsx правильным значением будет application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. Убедитесь, что это значение используется во всех местах, где устанавливается Content-Type.

  2. Ошибка в кодировке имени файла: Имя файла в заголовке Content-Disposition должно быть корректно закодировано. Используйте URLEncoder.encode для кодирования имени файла, чтобы избежать проблем с некорректными символами.

  3. Проблемы с потоками: Убедитесь, что данные корректно читаются из InputStream и не происходит потери данных в процессе передачи. Передавайте файл как поток, чтобы избежать переполнения памяти при больших объемах данных.

Обновленный код контроллера

Ниже приведен обновленный код вашего контроллера с исправлениями, указанными выше:

@GetMapping(value = "/export", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Resource> export(HttpServletResponse httpResponse) {
    try {
        List<MyOutPut> data = mydata();
        List<String> fieldsToInclude = List.of("id", "Name");
        ExportRequest<MyOutPut, ExcelDesignOption> request = new ExportRequest<>(
                ExportRequest.ExportFormat.EXCELSX, data, fieldsToInclude, MyOutPut.class, "export_data", new ExcelDesignOption()
        );

        System.out.println("Data received now");
        ExportUseCase<MyOutPut, ExcelDesignOption> exportUseCase = new ExportUseCase<>();
        ExportResponse response = exportUseCase.execute(request);

        HttpHeaders headers = new HttpHeaders();
        String encodedFileName = URLEncoder.encode(response.getFileName(), StandardCharsets.UTF_8.toString());
        headers.setContentDispositionFormData("attachment", encodedFileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        System.out.println("Data received now about to render from vendor");

        InputStreamResource file = new InputStreamResource(response.getInputStream());

        return ResponseEntity.ok()
                .headers(headers)
                .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
                .body(file);
    } catch (Exception e) {
        log.error("Search operation failed", e);
        throw new RuntimeException("Error exporting Excel file", e);
    }
}

Изменения в коде Angular

В вашем Angular приложении убедитесь, что вы правильно обрабатываете заголовки ответа. Убедитесь, что вы передаете ожидаемый тип Blob.

.downloadExcel(this.id).subscribe({
  next: ({ blob, filename }) => {
    this.myService.handleFileDownload(blob, filename);
  },
  error: (err) => console.error('File download failed', err)
});

return this.http.get(url, {
  observe: 'response',
  responseType: 'blob'
}).pipe(
  map(response => {
    const contentDisposition = response.headers.get('Content-Disposition');
    const filename = contentDisposition
      ? contentDisposition.split('filename="')[1].replace(/"/g, "")
      : 'Registered Vendor List.xlsx';

    return { blob: response.body as Blob, filename };
  })
);

Рекомендации

  1. Протестируйте файл локально: Убедитесь, что файл, который вы формируете, открывается на вашем локальном компьютере перед его загрузкой.

  2. Логи: Используйте логи для отладки проблемы и посмотрите, правильно ли файл формируется на сервере.

  3. Содержимое файла: Если возможно, конвертируйте файл непосредственно в памяти и сохраняйте его туда, чтобы убедиться, что файл не поврежден.

Если после всех изменений файл все еще не открывается, вам следует проверить, корректно ли генерируется и записывается содержимое файла перед передачей в ответ.

Оцените материал
Добавить комментарий

Капча загружается...