7

I want to download and save a file in local directory from server by Spring-OpenFeign with zero-copy.

Naive download method as following:

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())
}

In this code, data flow will be like this feign Internal Stream -> Buffer -> ByteArray -> Buffer -> File

How can I downalod and save a file memory-efficiently and faster way?

dgregory
  • 1,397
  • 1
  • 12
  • 26

1 Answers1

7

TL;DR. Use ResponseEntity<InputStreamResource> and Java NIO

According to SpringDecoder, Spring decode response using HttpMessageConverters

ResourceHttpMessageConverter which is one of HttpMesageConverters return InputStreamResource which contain InputStream and filename derived from Content-Disposition.

But, ResourceHttpMessageConverter must be initialized supportsReadStreaming = true (default value) If you have further interests on this implementation, check this code.

So, changed code are as followings:

@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 or less

Use 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)
}

Now, Feign Internal Stream -> File

dgregory
  • 1,397
  • 1
  • 12
  • 26