#java #spring #amazon-s3
Вопрос:
Я хочу предоставить API для загрузки содержимого файла корзины S3 в виде потока его потребителям. URL-адрес API похож на /файл загрузки/**, который является запросом GET.
- Каким должен быть мой тип возврата прямо сейчас, я попытался с помощью заголовка accept= application/octet-stream, который не сработал.
- Я не хочу записывать содержимое файла в какой-либо файл и отправлять его. Он должен быть возвращен в виде потока, вот и все.
Вот псевдокод контроллера, который я писал до сих пор, который постоянно выдает мне ошибку 406.
@GetMapping(value = "/downloadfile/**", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<Object> downloadFile(HttpServletRequest request) {
//reads the content from S3 bucket and returns a S3ObjectInputStream
S3ObjectInputStream object = null;
object = publishAmazonS3.getObject("12345bucket", "/logs/file1.log").getObjectContent();
return object
}
Есть какие-нибудь предложения о том, как это сделать, и что я делаю не так?
Ответ №1:
Я смог загрузить файл в виде потока, используя StreamingResponseBody
класс из Spring.
Вот код, который я использовал:
@GetMapping(value = "/downloadfile/**", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<S3ObjectInputStream> downloadFile(HttpServletRequest request) {
//reads the content from S3 bucket and returns a S3ObjectInputStream
S3Object object = publishAmazonS3.getObject("12345bucket", "/logs/file1.log");
S3ObjectInputStream finalObject = object.getObjectContent();
final StreamingResponseBody body = outputStream -> {
int numberOfBytesToWrite = 0;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = finalObject.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes..");
outputStream.write(data, 0, numberOfBytesToWrite);
}
finalObject.close();
};
return new ResponseEntity<>(body, HttpStatus.OK);
}
Способ проверить правильность потоковой передачи или нет-это загрузить файл размером около 400 МБ. Уменьшите размер Xmx до 256 МБ, передав параметры виртуальной машины. Теперь сравните функциональность загрузки с использованием и без использования StreamingResponseBody
, которую вы получите OutofMemoryError
при использовании обычных выходных потоков для написания контента
Комментарии:
1. Когда я пытаюсь это сделать, я получаю следующее: ошибка: несовместимые типы: не удается вывести аргументы типа для ResponseEntity<> вернуть новый ResponseEntity<><>(streamingResponseBody, HttpStatus. OK); ^ причина: переменная вывода T имеет несовместимые границы ограничения равенства: входной поток нижние границы: потоковый ответ.
Ответ №2:
Вы можете решить с помощью приведенного ниже примера
import java.io.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.grokonez.s3.services.S3Services;
@RestController
public class DownloadFileController {
@Autowired
S3Services s3Services;
/*
* Download Files
*/
@GetMapping("/api/file/{keyname}")
public ResponseEntity<byte[]> downloadFile(@PathVariable String keyname) {
ByteArrayOutputStream downloadInputStream = s3Services.downloadFile(keyname);
return ResponseEntity.ok().contentType(contentType(keyname))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="" keyname """)
.body(downloadInputStream.toByteArray());
}
private MediaType contentType(String keyname) {
String[] arr = keyname.split("\.");
String type = arr[arr.length - 1];
switch (type) {
case "txt":
return MediaType.TEXT_PLAIN;
case "png":
return MediaType.IMAGE_PNG;
case "jpg":
return MediaType.IMAGE_JPEG;
default:
return MediaType.APPLICATION_OCTET_STREAM;
}
}
}
Комментарии:
1. Чувак, код загружает файл в память здесь первым
.body(downloadInputStream.toByteArray());