Я пытаюсь загрузить большие файлы через AWS, не сохраняя их на сервере, и пока ничего из того, что я пробовал, не сработало.
Вот код, который нужно получить из AWS.
public ResponseInputStream<GetObjectResponse> downloadFile(String key){
return s3client.getObject(GetObjectRequest.builder().bucket(bucketName).key(key).build(),ResponseTransformer.toInputStream());
}
А вот и контроллер начальной загрузки, который пытается отправить этот поток ответов обратно.
@RequestMapping(path = "/download", method = GET)
public ResponseEntity<Resource> downloadOld(@RequestParam String key, Authentication authentication) throws IOException {
ResponseInputStream<GetObjectResponse> responseStream = this.amazonClient.downloadFile(key);
InputStreamResource resource = new InputStreamResource(responseStream);
HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + key.substring(key.lastIndexOf('/') + 1) + "\"");
return ResponseEntity.ok()
.headers(headers)
.contentLength(responseStream.response().contentLength())
.contentType(MediaType.parseMediaType(responseStream.response().contentType()))
.body(resource);
}
У меня частичный успех этого контроллера, у axios response.data действительно есть ответ.И текстовые файлы загружаются правильно. Однако, когда тип содержимого отличается от простого текста (например, application / pdf и т. Д.), Размер загружаемого файла в 1,5 раза превышает исходный размер файла.И само собой разумеется, что он поврежден.(pdf / zip / png) в моем приложении (приложении cordova) ничего не открывается.
Затем я попытался отправить StreamingResponseBody
.Вот два примера кода, которые я пробовал. На этот раз оба ответа axios пусты.
@Async
@RequestMapping(path = "/download", method = GET)
public ResponseEntity<StreamingResponseBody> download(@RequestParam String key, Authentication authentication) throws IOException {
ResponseInputStream<GetObjectResponse> responseStream = this.amazonClient.downloadFile(key);
final StreamingResponseBody out = (os) -> {
readAndWrite(responseStream, os);
};
HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + key.substring(key.lastIndexOf('/') + 1) + "\"");
headers.add("Content-Type", "application/pdf");
headers.add("Pragma", "no-cache");
headers.add("Cache-Control", "no-cache");
return (new ResponseEntity<>(out, headers, HttpStatus.OK));
}
Далее попытался отправить ответ без заголовка.
@Async
@RequestMapping(path = "/download", method = GET)
public StreamingResponseBody download(@RequestParam String key, Authentication authentication) throws IOException {
ResponseInputStream<GetObjectResponse> responseStream = this.amazonClient.downloadFile(key);
return (os) -> {
readAndWrite(responseStream, os);
};
}
Прямая реализация обратного вызова readAndWrite.
private void readAndWrite(final InputStream is, OutputStream os)
throws IOException {
byte[] data = new byte[2048];
int read = 0;
while ((read = is.read(data)) > 0) {
os.write(data, 0, read);
}
os.flush();
}
Вы можете только вообразить головные боли, которые я испытываю здесь.
Я также попробовал более новый асинхронный подход SDK в сборке, который снова не сработал.Вот клиентский вызов.
@Async
public CompletableFuture<ResponseBytes<GetObjectResponse>> downloadAsync(String key){
return s3AsyncClient.getObject(GetObjectRequest.builder().bucket(bucketName).key(key).build(), AsyncResponseTransformer.toBytes());
}
(Не удалось найти пример на сайте Amazon AWS о том, как вернуть поток. У них есть один пример, где они использовали ResponseTransformer для сохранения в локальный файл, который нене помогите мне).
Соответствующий контроллер для вышеуказанного AsyncResponse.
@Async
@RequestMapping(path = "/downloadAync", method = GET)
public CompletableFuture<ResponseBytes<GetObjectResponse>> downloadAsync(@RequestParam String key, Authentication authentication) throws IOException {
return this.amazonClient.downloadAsync(key);
}