4

I have a byte array as data. Now how do I write a controller method to return this byte array as file using Spring Boot? If I create a file out of this byte array data, then I should also take care of deleting it right?

Is there a way to send this byte array as file without having to physically create a file in my project, maybe send all bytes through the network or something?

However if that's not possible, is file creation, responding in rest api and then deleting it is the only way to solve this? My controller method would look like this in spring boot

@GetMapping("/download")
public ResponseEntity<Resource> download(String param) throws IOException {
    // Assume I already have this byte array from db or something
    Byte[] a = getItFromDB();

    // return it as a file without explicitly creating another file in my machine
    // I am ok with changing return type of this method from ResponseEntity to anything else if you have a solution
}
theprogrammer
  • 1,698
  • 7
  • 28
  • 48
  • 1
    Usually we create these files in temp of our web container and a crontab that schedule to delete them after 1 day ... – Mohsen Dec 19 '18 at 22:33
  • 1
    oh ok. that makes sense. But why delete after 1 day? What if we delete it immediately after returning it? – theprogrammer Dec 19 '18 at 22:36
  • 2
    that's because maybe a user download a file multipe times in a day and also you should wait till download finished and then you can delete the file, so you don't know how much does it takes client download the file( internet bandwith ... ) – Mohsen Dec 19 '18 at 22:39

4 Answers4

10

Just take the byte[] array, wrap it into a ByteArrayResource (which is an implementation of the Resource interface), build a ResponseEntity<Resource> from that, and return it.

@GetMapping("/download")
public ResponseEntity<Resource> download(String param) throws IOException {
    // Assume I already have this byte array from db or something
    byte[] array = getItFromDB();

    ByteArrayResource resource = new ByteArrayResource(array);
    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_OCTET_STREAM)
            .contentLength(resource.contentLength())
            .header(HttpHeaders.CONTENT_DISPOSITION,
                    ContentDisposition.attachment()
                        .filename("whatever")
                        .build().toString())
            .body(resource);
}

There is no need to create a temporary file, and no need to change the return type.

The media-type in the Content-Type header and the file-name in the Content-Disposition header are important hints for the web-browser (or whatever client) receiving your download. You should probably use better values than those in the code above. For example: for PNG-image content you would use MediaType.IMAGE_PNG and "whatever.png". Then the web-browser will probably open the system's favorite image viewer.

Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
  • 1
    I am curious why this answer is not upvoted or accepted? It does exactly the job that is asked for. – Emre Tapcı Oct 13 '21 at 12:59
  • This works fine, btw, you can also directly return ResponseEntity and do not need to wrap it in a ByteArrayResource. – maxeh Nov 24 '22 at 10:59
2

What is wrong with this?

public byte[] download(String param) throws IOException {
    return whateverByteArrayContenHere;
}
Antoniossss
  • 31,590
  • 6
  • 57
  • 99
  • 1
    Will I be able to download this as a file in the frontend? I mean with this byte[], we are not specifying anything about filename, or other file meta information right? What would be the best practice to send a file to frontend or any other client in general? – theprogrammer Dec 19 '18 at 22:29
  • 3
    @theprogrammer yes it works you can set filename in response header like this `response.setHeader("Content-disposition", "attachment; filename="+ fileName); ` – Mohsen Dec 19 '18 at 22:37
  • 1
    It all dependes on frontend interpretation. From server point of view it does not actually matters. All you can do is to set additional headers that can "hint" frontend on what the content actully is (after all it can be anything). – Antoniossss Dec 19 '18 at 22:50
2

Personally i do it with ResponseEntity something like this


@GetMapping(path = "/downloadFile")
public ResponseEntity<?> downloadFile (){

    HttpHeaders headers = new HttpHeaders();
    headers.set("Content-type", MediaType.IMAGE_JPEG_VALUE);
    headers.set("Content-Disposition","attachment; filename=\"whaterver.jpg\"")); // to view in browser change attachment to inline 

    return ResponseEntity.status(HttpStatus.OK).headers(headers).body(byteArrayFile);
}
sango
  • 151
  • 1
  • 10
1

As a normal way I think it's better that you create a temp file in your web container and send it as a file to client. And also you can clean your temp files base on your policy with a schedule crontab. For more information please visit save temp file and clean temp

Mohsen
  • 4,536
  • 2
  • 27
  • 49