最近遇到的一个需求,记录一下解决方案~
权限审批项目中有个场景是页面上展示多个附件,点击全部下载是下载全部文件,并将其下载成压缩包。
页面上展示的应该是多个链接,到时让前端传多个链接,后台进行获取文件服务器的文件进行压缩处理,再进行下载。
这里我选的是边压缩 ZIP 格式文件边下载(直接输出ZIP流)。
ZipOutPutStream 的使用:
以下是实现的示例 demo。
下载 Controller 实现,DownloadFileController.java
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author lixiaojin
* @date 2021/11/21 13:17
*/
@Api(tags = "文件相关接口")
@RestController
@RequestMapping("/api/v1/file_down")
@Slf4j
public class DownloadFileController {
/**
* 压缩本地文件
*/
@GetMapping("/download_1")
public void downloadOne(HttpServletResponse response) {
// 1、本地文件列表
List<File> files = new ArrayList<>();
files.add(new File("D:\\data\\test.png"));
files.add(new File("D:\\data\\test.png"));
files.add(new File("D:\\data\\Test.pdf"));
// 检查需要下载多文件列表中文件路径是否都存在
for (File file : files) {
if (!file.exists()) {
// 需要下载的文件中存在不存在地址
return;
}
}
// 2、响应头的设置
String downloadName = UUIDUtil.getUuid() + ".zip";
CompressDownloadUtil.setDownloadResponse(response, downloadName);
try {
// 3、将多个文件压缩写进响应的输出流
CompressDownloadUtil.compressZip(files, response.getOutputStream());
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
}
}
/**
* 压缩远程服务器文件
*/
@GetMapping("/download_2")
public void downloadTwo(HttpServletResponse response) {
// 1、文件路径,可以由前端传入
List<String> fileList = new ArrayList<>();
fileList.add("http://199.199.2.135/filer/2021-11/test_20211119160019989.png");
fileList.add("http://199.199.2.135/filer/2021-11/test_20211119160019989.png");
fileList.add("http://199.199.2.135/filer/2021-11/确认表_20211112173714929.pdf");
//-- 2、响应头的设置
String downloadName = UUIDUtil.getUuid() + ".zip";
CompressDownloadUtil.setDownloadResponse(response, downloadName);
try {
//-- 3、将多个文件压缩写进响应的输出流
CompressDownloadUtil.compressZipByName(fileList, response.getOutputStream());
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
}
}
}
压缩文件工具类,
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @author lixiaojin
* @date 2021/11/21 13:30
*/
@Slf4j
public class CompressDownloadUtil {
private CompressDownloadUtil() {}
/**
* 设置下载响应头
*/
public static HttpServletResponse setDownloadResponse(HttpServletResponse response, String downloadName) {
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;fileName*=UTF-8''"+ downloadName);
return response;
}
public static Integer[] toIntegerArray(String param) {
return Arrays.stream(param.split(","))
.map(Integer::valueOf)
.toArray(Integer[]::new);
}
/**
* 将多个文件压缩到指定输出流中
*
* @param files 需要压缩的文件列表
* @param outputStream 压缩到指定的输出流
*/
public static void compressZip(List<File> files, OutputStream outputStream) {
// 包装成ZIP格式输出流
try (ZipOutputStream zipOutStream = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
// 设置压缩方法
zipOutStream.setMethod(ZipOutputStream.DEFLATED);
// 将多文件循环写入压缩包
for (int i = 0; i < files.size(); i++) {
File file = files.get(i);
FileInputStream filenputStream = new FileInputStream(file);
byte[] data = new byte[(int) file.length()];
filenputStream.read(data);
// 添加ZipEntry,并ZipEntry中写入文件流,这里,加上i是防止要下载的文件有重名的导致下载失败
zipOutStream.putNextEntry(new ZipEntry(i+ "-" + file.getName()));
zipOutStream.write(data);
filenputStream.close();
zipOutStream.closeEntry();
}
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
} finally {
try {
if (Objects.nonNull(outputStream)) {
outputStream.close();
}
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
}
}
}
private static HttpResponse invokeGetFile(String url, CloseableHttpClient httpclient) {
HttpResponse response;
try {
HttpGet get = new HttpGet(url);
get.setHeader("Content-type", "UTF-8");
get.setHeader("Connection", "close");
response = httpclient.execute(get);
} catch (Exception e) {
log.error("请求文件异常");
return null;
}
return response;
}
public static void compressZipByName(List<String> files, OutputStream outputStream) {
// 包装成ZIP格式输出流
try (ZipOutputStream zipOutStream = new ZipOutputStream(new BufferedOutputStream(outputStream))) {
// 设置压缩方法
zipOutStream.setMethod(ZipOutputStream.DEFLATED);
// 将多文件循环写入压缩包
for (int i = 0; i < files.size(); i++) {
CloseableHttpClient httpclient = HttpClients.createMinimal();
String fileName = getFileName(files.get(i));
HttpResponse response = invokeGetFile(files.get(i), httpclient);
InputStream inputStream = response.getEntity().getContent();
response.getEntity().getContentLength();
byte[] data = new byte[(int) response.getEntity().getContentLength()];
inputStream.read(data);
// 添加ZipEntry,并ZipEntry中写入文件流,这里,加上i是防止要下载的文件有重名的导致下载失败
zipOutStream.putNextEntry(new ZipEntry(i+ "-" + fileName));
zipOutStream.write(data);
inputStream.close();
zipOutStream.closeEntry();
httpclient.close();
}
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
} finally {
try {
if (Objects.nonNull(outputStream)) {
outputStream.close();
}
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadallfiles", e);
}
}
}
/**
* 获取文件名
* @param fileUrl 文件地址
* @return 文件名
*/
private static String getFileName(String fileUrl) {
String[] nameArr = fileUrl.split("/");
return nameArr[nameArr.length - 1];
}
/**
* 下载文件
* @param outputStream 下载输出流
* @param zipFilePath 需要下载文件的路径
*/
public static void downloadFile(OutputStream outputStream, String zipFilePath) {
File zipFile = new File(zipFilePath);
if (!zipFile.exists()) {
// 需要下载压塑包文件不存在
return ;
}
try (FileInputStream inputStream = new FileInputStream(zipFile)) {
byte[] data = new byte[(int) zipFile.length()];
inputStream.read(data);
outputStream.write(data);
outputStream.flush();
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadZip", e);
} finally {
try {
if (Objects.nonNull(outputStream)) {
outputStream.close();
}
} catch (IOException e) {
log.error(CompressDownloadUtil.class.getName(), "downloadZip", e);
}
}
}
/**
* 删除指定路径的文件
* @param filepath 文件路径
*/
public static void deleteFile(String filepath) {
File file = new File(filepath);
deleteFile(file);
}
/**
* 删除指定文件
* @param file 文件
*/
public static void deleteFile(File file) {
// 路径为文件且不为空则进行删除
if (file.isFile() && file.exists()) {
file.delete();
}
}
}
访问接口: http://localhost:9003/api/v1/file_down/download_1 http://localhost:9003/api/v1/file_down/download_2
测试完成~
responseType: ‘blob’
,这一步是关键,写了也看检查一下是不是生效了,不然下载问题时就有问题。End.
Copyright: 采用 知识共享署名4.0 国际许可协议进行许可 Links: https://lixj.fun/archives/java实现多文件压缩下载