SpringBoot压缩多个文件后下载

经常遇到需求,服务器要批量压缩一个或者多个文件,响应给客户端。这里演示一个Demo。

客户端通过参数指定服务器上的某个文件夹,服务器对该文件夹进行zip压缩后响应给客户端。

controller层代码:

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/download")
public class DownloadController {
@GetMapping
public void download(HttpServletRequest request,
HttpServletResponse response,
@RequestParam("folder") String folder) throws UnsupportedEncodingException {
Path folderPath = Paths.get(folder);
if (!Files.isDirectory(folderPath)) {
// 文件夹不存在
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return ;
}
// 二进制数据流
response.setContentType("application/octet-stream");
// 附件形式打开
response.setHeader("Content-Disposition", "attachment; filename=" + new String((folderPath.getFileName().toString() +  ".zip").getBytes("GBK"),"ISO-8859-1"));
try (ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream())){
LinkedList<String> path = new LinkedList<>();
Files.walkFileTree(folderPath, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
// 开始遍历目录
if (!dir.equals(folderPath)) {
path.addLast(dir.getFileName().toString());
// 写入目录 
ZipEntry zipEntry = new ZipEntry(path.stream().collect(Collectors.joining("/", "", "/")));
try {
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// 开始遍历文件
try (InputStream inputStream = Files.newInputStream(file)) {
// 创建一个压缩项,指定名称
String fileName = path.size() > 0 
? path.stream().collect(Collectors.joining("/", "", "")) + "/" + file.getFileName().toString()
: file.getFileName().toString();
ZipEntry zipEntry = new ZipEntry(fileName);
// 添加到压缩流
zipOutputStream.putNextEntry(zipEntry);
// 写入数据
int len = 0;
// 10kb缓冲区
byte[] buffer = new byte[1024 * 10];
while ((len = inputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
zipOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// 结束遍历目录
if (!path.isEmpty()) {
path.removeLast();
}
return FileVisitResult.CONTINUE;
}
});
zipOutputStream.closeEntry();
} catch (IOException e) {
e.printStackTrace();
} 
}
}

演示

下载本地磁盘的 D:\\Nginx 目录,注意文件路径参数有特殊符号,需要进行uri编码。

http://localhost/download?folder=D%3a%5c%5cnginx-1.18.0

对比原始文件夹和下载后解压开来的文件夹,大小一致说明没问题。

23cd6d56b03280a64ce28f68b4f065bbde1848c6.png

qrcode