如何通过Spring-Feign获得InputStream?

9

我想通过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内部流 -> 缓冲区 -> 字节数组 -> 缓冲区 -> 文件

我该如何以更内存高效且更快的方式下载并保存文件?

2个回答

9

简述:使用ResponseEntity<InputStreamResource>和Java NIO

SpringDecoder显示,Spring使用HttpMessageConverters进行响应解码。

其中的ResourceHttpMessageConverter是HttpMesageConverters之一,返回包含InputStream和从Content-Disposition中获取的文件名的InputStreamResource

但是,ResourceHttpMessageConverter必须初始化supportsReadStreaming = true(默认值)。如果您对此实现有进一步的兴趣,请查看此代码

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

@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或更早版本

使用Guava的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内部流转换为文件

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实现不同,这是一个已经打开的资源的描述符 - 因此从isOpen()方法返回true。
如果你需要将资源描述符保存在某个地方,或者需要多次从流中读取,请不要使用InputStreamResource。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接