如何通过Spring-Feign获取InputStream?

问题描述 投票:0回答:2

我想通过Spring-OpenFeign以零拷贝的方式从服务器下载并保存一个文件到本地目录。

简单的下载方法如下:

import org.apache.commons.io.FileUtils

@GetMapping("/api/v1/files")
ResponseEntity<byte[]> getFile(@RequestParam(value = "key") String key) {
    ResponseEntity<byte[]> resp = getFile("filename.txt")
    File fs = new File("/opt/test")
    FileUtils.write(file, resp.getBody())
}

在这段代码中,数据流将是这样的

feign Internal Stream -> Buffer -> ByteArray -> Buffer -> File

如何以更高效、更快速的方式下载并保存文件?

java spring-boot spring-cloud-feign feign zero-copy
2个回答
8
投票

TL;博士。使用
ResponseEntity<InputStreamResource>
和 Java NIO

根据SpringDecoder,Spring使用HttpMessageConverters解码响应

ResourceHttpMessageConverter 是 HttpMesageConverters 之一,返回 InputStreamResource,其中包含从

Content-Disposition
派生的 InputStream 和文件名。

但是,ResourceHttpMessageConverter 必须初始化

supportsReadStreaming = true (default value)
如果您对此实现有进一步的兴趣,请检查此代码

因此,更改后的代码如下:

@GetMapping("/api/v1/files")
ResponseEntity<InputStreamResource> getFile(@RequestParam(value = "key") String key)

JDK9

try (OutputStream os = new FileOutputStream("filename.txt")) {
    responeEntity.getBody().getInputStream().transferTo(os);
}

JDK8 或以下

使用番石榴ByteStreams.copy()

Path p = Paths.get(responseEntity.getFilename())
ReadableByteChannel rbc = Channels.newChannel(responeEntity.getBody().getInputStream())
try(FileChannel fc = FileChannel.open(p, StandardOpenOption.WRITE)) {
    ByteStreams.copy(rbc, fc)
}

现在,

Feign Internal Stream -> File


0
投票

假设您想使用 Feign 从外部 api 获取流。 您应该在 feign 方法中使用

ByteArrayResource
作为响应类型。

在你的 feign 接口中让你的方法返回

ByteArrayResource
来消费流

@FeignClient(name = "lim-service", url = "https://your-api.com/api)
public interface LimClient {

    @GetMapping("/api/v1/files")
    ByteArrayResource getFile(@RequestParam(value = "key") String key);
}

关于已接受答案的评论:

仅当流仅被消耗一次时,您才可以使用

InputStreamResource
代替
ByteArrayResource
,如果您需要多次消耗流,则不应使用
InputStreamResource

引用

InputStreamResource
javadoc

仅当没有其他具体

Resource
实施适用时才应使用。特别是,在可能的情况下,更喜欢
ByteArrayResource
或任何基于文件的
Resource
实现。

与其他

Resource
实现相比,这是一个已打开资源的描述符 - 因此从
true
返回
isOpen()

如果您需要将资源描述符保留在某处,或者

如果您需要多次从流中读取
,请不要使用InputStreamResource

© www.soinside.com 2019 - 2024. All rights reserved.