一、最终结果
二、导入pom.xm相关依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
三、FileUtil工具类
package com.yf.client.util.zipFileUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.yf.utils.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 文件下载压缩工具类(zip)
*
* @author FengQing
* @program fengqing
* @description
* @date 2024/04/25
*/
public class FileUtil {
// .zip文件名称
private static final String ZIP_FILE_NAME = "output.zip";
private static String URL;
/**
* 创建文件夹并复制文件
* @param sourceFilePath 源文件路径
* @param mkDirName 文件夹名称
* @param existingFolders 临时文件存储列表
* @throws IOException
*
* 创建虚拟文件对象 路径:/javaProc/oss/广州新居网家居科技有限公司_A0153_32802_20240429154040A006.xlsx
*/
public static File copyFile(String sourceFilePath, String mkDirName, List<String> existingFolders) throws IOException {
// 创建文件夹
String url = StringUtils.substringBeforeLast(sourceFilePath, "\\") + "\\"; //开发本地使用“\\”
//String url = StringUtils.substringBeforeLast(sourceFilePath, "/") + "/"; // 生产环境使用“/”
URL = url;
String folderPath = createMkDirs(url + mkDirName, existingFolders);
File sourceFile = new File(sourceFilePath);
File destFile = new File(folderPath + File.separator + sourceFile.getName());
Files.copy(sourceFile.toPath(), destFile.toPath());
return destFile;
}
/**
* 创建文件夹
* @param mkDirName 文件夹名称
* @param existingFolders 件夹列表暂存区
* @return 创建的文件夹路径
*/
public static String createMkDirs(String mkDirName, List<String> existingFolders){
String folderPath = mkDirName; // 新文件夹路径
existingFolders.add(mkDirName);
File folder = new File(mkDirName);
if (!folder.exists()) {
folder.mkdirs(); // 创建文件夹
}
return folderPath;
}
/**
* 压缩文件列表为ZIP文件并下载
* @param toBeZippedFiles 文件列表
* @param response HttpServletResponse对象
* @throws Exception
*/
public static void zipFiles(List<File> toBeZippedFiles, HttpServletResponse response) throws Exception {
byte[] buffer = new byte[1024];
try (FileOutputStream fos = new FileOutputStream(URL + ZIP_FILE_NAME);
ZipOutputStream zos = new ZipOutputStream(fos)) {
for (File file : toBeZippedFiles) {
try (FileInputStream fis = new FileInputStream(file)) {
// 获取相对路径,保留文件夹结构
String relativePath = getRelativePath(file);
ZipEntry ze = new ZipEntry(relativePath);
zos.putNextEntry(ze);
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
}
}
}
getOutputStream(buffer, response);
}
/**
* 将ZIP文件写入HttpServletResponse输出流进行下载
* @param buffer 缓冲区
* @param response HttpServletResponse对象
* @throws Exception
*/
private static void getOutputStream(byte[] buffer, HttpServletResponse response) throws Exception {
try (FileInputStream fis = new FileInputStream(URL + ZIP_FILE_NAME);
BufferedInputStream bis = new BufferedInputStream(fis);
OutputStream os = response.getOutputStream()) {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + "output.zip");
int len;
while ((len = bis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
}
}
/**
* 删除临时文件和文件夹(可选)
* @param existingFolders 临时文件夹路径列表
*/
public static void deleteMkdirs(List<String> existingFolders){
if (CollectionUtil.isNotEmpty(existingFolders)) {
for (String folderPath : existingFolders) {
File folder = new File(folderPath);
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
file.delete();
}
}
folder.delete();
}
}
}
/**
* 删除临时文件和文件夹(可选)
* @param files 临时文件夹路径列表
*/
public static void deleteTempFiles(List<File> files) {
for (File file : files) {
if (!file.delete()) {
// 删除失败,输出错误信息
System.err.println("Failed to delete temp file: " + file.getAbsolutePath());
}
}
}
/**
* 获取相对路径,保留文件夹结构
* @param file 文件
* @return 相对路径
*/
public static String getRelativePath(File file) {
String basePath = URL; // 基础路径
String filePath = file.getAbsolutePath();
if (filePath.startsWith(basePath)) {
// 返回去除基础路径后的相对路径
return filePath.substring(basePath.length());
} else {
// 如果路径不在基础路径下,直接返回文件名
return file.getName();
}
}
/**
* 截取“去除桶名称”,保留相对路径
* @param url
* @return
*/
public static String extractFilePath(String url) {
int rootIndex = url.indexOf("/", 1); // 从第二个字符开始查找斜杠
return rootIndex != -1 ? url.substring(rootIndex + 1) : null;
}
/**
* 读取文件流解析到文件名称集
* @param file
* @return
* @throws IOException
*/
public static List<String> getEasyExcelFileNames(MultipartFile file) throws IOException {
EasyExcelUtil listener = new EasyExcelUtil();
EasyExcel.read(file.getInputStream(), listener).sheet().doRead();
List<Map<Integer, String>> targetColumnData = listener.getTargetColumnData();
return targetColumnData.stream()
.map(v -> v.get(0))
.distinct()
.collect(Collectors.toList());
}
}
四、业务层impl处理
/**
* 文件筛查(自动下载)接口(Excel文件解析处理)
* @return
* @throws Exception
*/
@Override
public void downloadFileScreen(MultipartFile file, HttpServletResponse response) throws Exception {
log.info("文件名称集file:{}", file.getOriginalFilename());
// 这里 只要,然后读取第一个sheet 同步读取会自动finish
//List<String> easyExcelFileNames = FileUtil.getEasyExcelFileNames(file);
// 1、上传文件名称:根据文件名称查询数据库中文件 URL 地址
/*List<String> urlList = this.selectFileScreenList(easyExcelFileNames);
if (StringUtils.isNull(urlList) || urlList.size() == 0) {
throw new RuntimeException("系统中未查询到文件地址信息!");
}*/
// log.info("数据库查询url地址集:{}", urlList);
List<String> urlList = new ArrayList<>();
String fileName = "/file-screen-bucket/2024/04/30/xxxx02有限公司_A34947_32769_20240430082633A005.xlsx"; // Excel文件路径
String fileName1 = "/file-screen-bucket/2024/04/30/xxxxxx01有限公司_A34946_6_20240430082624A004.xlsx"; // Excel文件路径
urlList.add(fileName);
urlList.add(fileName1);
// 2、遍历文件,解析Excel数据进行判断,存入不同文件夹中或输出.zip文件
List<String> existingFolders = new ArrayList<>();
List<java.io.File> toBeZippedFiles = new ArrayList<>();
List<java.io.File> tempFiles = new ArrayList<>(); // 存储临时文件
for (String url : urlList) {
//截取“去除桶名称”,保留相对路径
String extractedPath = FileUtil.extractFilePath(url);
if (extractedPath != null) {
// 根据相对路径读取minio文件流
byte[] bytes = localSysFileFeignService.getExtractedPath(extractedPath);
// 创建临时文件(截取原文件名称)
java.io.File tempFile = new java.io.File(StringUtils.substringAfterLast(extractedPath, "/"));
tempFiles.add(tempFile);
try (InputStream inputStream = new ByteArrayInputStream(bytes);
FileOutputStream outputStream = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
// 创建虚拟文件对象
String absolutePath = tempFile.getAbsolutePath();
log.info("创建虚拟文件对象 路径:{}", absolutePath);
EasyExcelUtil listener = new EasyExcelUtil();
EasyExcel.read(absolutePath, listener).sheet().doRead();
List<Map<Integer, String>> targetColumnData = listener.getTargetColumnData();
List<String> addList = targetColumnData.stream()
.map(v -> v.get(1))
.distinct()
.collect(Collectors.toList());
log.info("去重后的数量:{}", addList.size());
// 符合条件存入不同文件夹中
if (addList.size() < 20) {
java.io.File destFile = FileUtil.copyFile(absolutePath, "小于20", existingFolders);
// 复制的文件添加到待压缩文件列表将表
toBeZippedFiles.add(destFile); // 将复制的文件添加到待压缩文件列表
} else if (addList.size() > 20) {
java.io.File destFile = FileUtil.copyFile(absolutePath, "大于20", existingFolders);
toBeZippedFiles.add(destFile); // 将复制的文件添加到待压缩文件列表
} else {
toBeZippedFiles.add(new java.io.File(absolutePath)); // 不符合条件的文件直接添加到待压缩文件列表
}
} else {
// 处理文件路径不合法的情况
System.out.println("文件路径不合法");
}
}
log.info("tempFiles临时文件 路径:{}", tempFiles);
log.info("toBeZippedFiles列表 路径:{}", toBeZippedFiles);
log.info("existingFolders列表 路径:{}", existingFolders);
// 3、将待压缩的文件打包成zip并添加到输出.zip文件
FileUtil.zipFiles(toBeZippedFiles, response);
// 4、删除临时文件和文件夹(可选)
FileUtil.deleteMkdirs(existingFolders);
// 5、删除(创建临时文件)
FileUtil.deleteTempFiles(tempFiles);
FileUtil.deleteTempFiles(toBeZippedFiles);
}
五、控制层Controller
/**
* 文件筛查(自动下载)接口(Excel文件解析处理)
* @return
*/
@ApiOperation(value = "文件筛查自动下载接口", notes = "文件筛查自动下载接口")
@PostMapping("/download")
public void downloadFileScreen(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws Exception {
iFileScreenService.downloadFileScreen(file, response);
}
调用示例: