Вопрос или проблема
Я использую следующий код в своем контроллере
@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, необходимо учесть несколько аспектов, которые могут влиять на формирование и передачу файла.
Возможные причины проблемы
-
Несоответствующий Content-Type: В заголовках ответа Content-Type должен четко соответствовать типу файла. Для Excel файлов формата .xlsx правильным значением будет
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
. Убедитесь, что это значение используется во всех местах, где устанавливается Content-Type. -
Ошибка в кодировке имени файла: Имя файла в заголовке Content-Disposition должно быть корректно закодировано. Используйте
URLEncoder.encode
для кодирования имени файла, чтобы избежать проблем с некорректными символами. -
Проблемы с потоками: Убедитесь, что данные корректно читаются из 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 };
})
);
Рекомендации
-
Протестируйте файл локально: Убедитесь, что файл, который вы формируете, открывается на вашем локальном компьютере перед его загрузкой.
-
Логи: Используйте логи для отладки проблемы и посмотрите, правильно ли файл формируется на сервере.
-
Содержимое файла: Если возможно, конвертируйте файл непосредственно в памяти и сохраняйте его туда, чтобы убедиться, что файл не поврежден.
Если после всех изменений файл все еще не открывается, вам следует проверить, корректно ли генерируется и записывается содержимое файла перед передачей в ответ.