我想通过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
如何以更高效、更快速的方式下载并保存文件?
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)
try (OutputStream os = new FileOutputStream("filename.txt")) {
responeEntity.getBody().getInputStream().transferTo(os);
}
使用番石榴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
假设您想使用 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
。