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