如何通过Spring-Feign获取InputStream?

jjjwad0x  于 2023-11-17  发布在  Spring
关注(0)|答案(2)|浏览(687)

我想用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
我怎样才能下载和保存一个文件内存有效和更快的方式?

db2dz4w8

db2dz4w81#

TL; DR.使用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)

字符串

JDK 9

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

JDK 8以下

使用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 Internal Stream -> File

soat7uwm

soat7uwm2#

假设你想使用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 classjavadoc
只有在没有其他特定的Resource实现适用的情况下才应该使用。特别是,如果可能,首选ByteArrayResource或任何基于文件的Resource实现。
与其他Resource实现不同,这是一个 * 已打开 * 资源的描述符-因此从isOpen()返回true
如果需要将资源描述符保存在某处,或者如果需要多次从流中读取,请不要使用InputStreamResource

相关问题