简体   繁体   English

在 Spring 引导中将 AWS S3 文件下载为 stream

[英]Downloading AWS S3 file as a stream in Spring boot

I want to expose an API to download a S3 bucket file content as stream to its consumers.我想公开一个 API 以将 S3 存储桶文件内容作为 stream 下载给它的消费者。 The API URL is like /downloadfile/** which is GET request. API URL 就像 /downloadfile/** 是 GET 请求。

  1. What should be my return type right now I tried with accept header= application/octet-stream which didn't work.我现在尝试的返回类型应该是什么 accept header= application/octet-stream 不起作用。
  2. I don't want to write the content of file to any file and send it.我不想将文件的内容写入任何文件并发送。 It should be returned as a stream that's it.它应该作为 stream 返回。

Here is the controller pseudo code I wrote till now which is giving me 406 error all the time.这是我到目前为止编写的 controller 伪代码,它一直给我 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
    }

Any suggestions here on the way of doing this and what I am doing wrong?关于这样做的方式以及我做错了什么,这里有什么建议吗?

I was able to download the file as a stream by using StreamingResponseBody class from Spring.通过使用来自 Spring 的StreamingResponseBody class,我能够将文件下载为 stream。

Here is the code I used:这是我使用的代码:

    @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);
    }

The way to test the streaming is done correctly or not is to have a file of around 400mb to download.测试流媒体是否正确完成的方法是下载一个大约 400mb 的文件。 Reduce your Xmx to 256mb by passing the in the vm options.通过传入 vm 选项将 Xmx 减少到 256mb。 Now, compare the download functionality with and without using StreamingResponseBody , you will get OutofMemoryError when using the conventional OutputStreams for writing the content现在,比较使用和不使用StreamingResponseBody的下载功能,当使用常规的 OutputStreams 写入内容时,您将得到OutofMemoryError

You can solve with the below example你可以用下面的例子来解决

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;
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM