This article provides guidance on how to download a single file, download a Gzip-compressed file, and download multiple files through a zip archive in a Spring Boot application.

Download a single file

The key points are as follows.

  • Get the size of the file.
  • Get the media type (Content-Type) of the file.
  • Construct the Content-Disposition header with the ContentDisposition utility class to avoid the problem of downloading file name gibberish.
  • Copy data to the client.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package io.springcloud.controller;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/download")
public class DownloadController {

    @GetMapping
    public void download (HttpServletRequest request, HttpServletResponse response) throws IOException {
        
        // The file to be downloaded.
        Path file = Paths.get("E:\\test.mp4");
        
        // Get the media type of the file
        String contentType = Files.probeContentType(file);
        if (contentType == null) {
            // Use the default media type
            contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        
        response.setContentType(contentType);
        // File Size
        response.setContentLengthLong(Files.size(file));
        /**
            * Building the Content-Disposition header with the ContentDisposition utility class can avoid the problem of garbled downloaded file names.
            */
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
                                                                            .filename(file.getFileName().toString(), StandardCharsets.UTF_8)
                                                                            .build()
                                                                            .toString());
        // Response data to the client
        Files.copy(file, response.getOutputStream());
    }
}

Compressing files with Gzip

If the downloaded file is particularly large, you can consider using gzip to compress it and then download it, which can reduce the transfer bytes and save traffic. However, using gzip encoding will consume extra CPU.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package io.springcloud.controller;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.GZIPOutputStream;

import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/download")
public class DownloadController {

    @GetMapping("/gzip")
    public void gzipDownload (HttpServletRequest request, HttpServletResponse response) throws IOException {
        
        Path file = Paths.get("E:\\test.mp4");
        
        String contentType = Files.probeContentType(file);
        if (contentType == null) {
            contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }
        
        // Tell the client what encoding is used by the body and the client will automatically decode it.
        response.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
        response.setContentType(contentType);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
                                                                            .filename(file.getFileName().toString(), StandardCharsets.UTF_8)
                                                                            .build()
                                                                            .toString());
        
        // Compress the body with GZIPOutputStream
        try(GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())){
            Files.copy(file, gzipOutputStream);
        }
    }
}

Download multiple files

If multiple files need to be downloaded at once, then the server needs to archive multiple files into a single zip file and then respond the zip file to the client.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package io.springcloud.controller;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Controller
@RequestMapping("/download")
public class DownloadController {

    @GetMapping("/zip")
    public void zipDownload (HttpServletRequest request, HttpServletResponse response) throws IOException {
        
        // List of files to be downloaded
        List<Path> files = Arrays.asList(Paths.get("E:\\test.mp4"), 
                                    Paths.get("E:\\node.txt"), 
                                    Paths.get("E:\\keys.txt"));
        
        
        response.setContentType("application/zip"); // zip archive format
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
                                                                            .filename("download.zip", StandardCharsets.UTF_8)
                                                                            .build()
                                                                            .toString());
        
        
        // Archiving multiple files and responding to the client
        try(ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())){
            for (Path file : files) {
                try (InputStream inputStream = Files.newInputStream(file)) {
                    zipOutputStream.putNextEntry(new ZipEntry(file.getFileName().toString()));
                    StreamUtils.copy(inputStream, zipOutputStream);
                    zipOutputStream.flush();
                }
            }
        }
    }
}