Загрузка файла AWS S3 в виде потока при весенней загрузке

#java #spring #amazon-s3

Вопрос:

Я хочу предоставить API для загрузки содержимого файла корзины S3 в виде потока его потребителям. URL-адрес API похож на /файл загрузки/**, который является запросом GET.

  1. Каким должен быть мой тип возврата прямо сейчас, я попытался с помощью заголовка accept= application/octet-stream, который не сработал.
  2. Я не хочу записывать содержимое файла в какой-либо файл и отправлять его. Он должен быть возвращен в виде потока, вот и все.

Вот псевдокод контроллера, который я писал до сих пор, который постоянно выдает мне ошибку 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());