一.单个压缩包制作
package com.stream.core.util;
import com.alibaba.excel.util.CollectionUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 打包下载
* @author WYG
*
*/
@Slf4j
public class ZipUtil {
/**
* 将文件打成压缩包操作
* @param fileList
* @throws IOException
*/
public static void zipFile(List<File> fileList, String zipPath) {
// 获取文件压缩包输出流
try (
OutputStream outputStream = new FileOutputStream(zipPath);
CheckedOutputStream checkedOutputStream = new CheckedOutputStream(outputStream,new Adler32());
ZipOutputStream zipOut = new ZipOutputStream(checkedOutputStream)){
for (File file : fileList) {
// 获取文件输入流
InputStream fileIn = new FileInputStream(file);
// 使用 common.io中的IOUtils获取文件字节数组
byte[] bytes = IOUtils.toByteArray(fileIn);
// 写入数据并刷新
zipOut.putNextEntry(new ZipEntry(file.getName()));
zipOut.write(bytes,0,bytes.length);
zipOut.flush();
fileIn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
二.多级压缩包:将附件打成压缩包后,再将多个压缩包进行压缩,压缩成大压缩包
package com.stream.invoice.task;
import com.stream.core.util.*;
import com.stream.invoice.bo.TEleCertXbrlPackageBO;
import com.stream.invoice.enums.InvoiceEleCertTypeEnums;
import com.stream.storage.bo.StorageUploadRspBO;
import com.stream.storage.handler.EcrStorageHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Component
public class EleCertXbrlPackageTask {
@Autowired
protected EcrStorageHandler ecrStorageHandler; // 影像上传处理器
/**
* 压缩包执行
*
* @param yearMonth 年月
* @param taxPayerNo 税号
* @param eleCertXbrlDataMap xbrl文件数据通过唯一标识进行分组
*/
private void downloadZipFile(String yearMonth, String taxPayerNo, List<File> fileList, Map<String, List<TEleCertXbrlPackageBO>> eleCertXbrlDataMap) {
/** 一级压缩包 **/
List<ZipUtil.ZipPo> list = new ArrayList<>();
StringBuffer str = new StringBuffer();
/** 将相同唯一标识的文件打包到同一个压缩包里 **/
MapUtils.forEach(eleCertXbrlDataMap, (key, dataList) -> {
/* 封装小压缩包(二级压缩包) */
List<ZipUtil.ZipPo> zipPoList = new ArrayList<>();
String zipName = "";
/* 记录xbrl文件名称是否有相同的,如果有相同的,则在文件后面加上数字,依次递增 key:文件名 value:记录数字 */
Map<String, Object> zipParaMap = new HashMap<>();
for (TEleCertXbrlPackageBO certXbrlBO : dataList) {
/* 获取票据类型 */
if (StringUtil.isEmpty(zipName)) {
List<InvoiceEleCertTypeEnums> typeEnumsList = Arrays.stream(InvoiceEleCertTypeEnums.values()).filter(row -> certXbrlBO.getFileName().contains(row.getZipName())).collect(Collectors.toList());
zipName = typeEnumsList.get(0).getZipName();
}
if (StringUtil.isNotEmpty(certXbrlBO.getFileCode())) {
StorageUploadRspBO storageUploadRspBO = new StorageUploadRspBO();
try {
storageUploadRspBO = ecrStorageHandler.queryExample(certXbrlBO.getFileCode());
// 判断xbrl文件名称是否有相同的,如果有相同的,则在文件后面加上数字,依次递增
if (zipParaMap.containsKey(certXbrlBO.getFileName())) {
String suffix = certXbrlBO.getFileName().split("\\.")[certXbrlBO.getFileName().split("\\.").length - 1];
String name = certXbrlBO.getFileName().split("\\.")[certXbrlBO.getFileName().split("\\.").length - 2];
String num = zipParaMap.get(certXbrlBO.getFileName()).toString();
// 文件名称
String fileName = FileUtil.getResourcesPath() + File.separator + name + "(" + num + ")." + suffix;
File zipFile = FileUtil.downloadFilePath(fileName, storageUploadRspBO.getDownloadUrl());
fileList.add(zipFile); // 收集xbrl文件地址,方便后续删除
zipPoList.add(new ZipUtil.ZipPo(name + "(" + num + ")." + suffix, zipFile.getPath()));
zipParaMap.put(certXbrlBO.getFileName(), Integer.valueOf(num) + 1);
} else {
zipParaMap.put(certXbrlBO.getFileName(), 1);
File zipFile = FileUtil.downloadFilePath(certXbrlBO.getFileName(), storageUploadRspBO.getDownloadUrl());
fileList.add(zipFile); // 收集xbrl文件地址,方便后续删除
zipPoList.add(new ZipUtil.ZipPo(certXbrlBO.getFileName(), zipFile.getPath()));
}
} catch (Exception e) {
str.append("【" + certXbrlBO.getFileName() + "附件获取失败!: " + e.getMessage() + "】");
}
}
}
zipName = zipName + "_receiver_" + DateUtils.getCurrentSimpDate() + "_" + key.split("\\.")[key.split("\\.").length - 2] + ".zip";
list.add(new ZipUtil.ZipPo(zipName, zipPoList)); // 用唯一标识作为小压缩包名称
});
/* 记录压缩失败的数据如果有压缩失败的数据,则在压缩包里面添加 压缩包打包失败数据.txt 提示有文件可能没打上包*/
String path = FileUtil.getResourcesPath() + "压缩包打包失败数据.txt";
if (StringUtil.isNotEmpty(String.valueOf(str))) {
try {
File newTextFile = new File(path);
FileWriter fileWriter = new FileWriter(newTextFile);
fileWriter.write(String.valueOf(str));
fileWriter.close();
fileList.add(newTextFile); // 收集xbrl文件地址,方便后续删除
list.add(new ZipUtil.ZipPo("压缩包打包失败数据.txt", newTextFile.getPath()));
} catch (Exception e) {
e.printStackTrace();
}
}
String yyyyMM = yearMonth.split("-")[0] + yearMonth.split("-")[1];
String newZipName = "receiver_" + taxPayerNo + "_" + yyyyMM + ".zip";
log.info("一级压缩包名称: " + newZipName);
/** 判断压缩包是否存在,如果存在则删除 **/
File zipNewFile = new File(FileUtil.getZipFilePath(newZipName));
log.info("判断压缩包是否存在: " + zipNewFile.exists());
if (zipNewFile.exists()) {
zipNewFile.delete();
}
String base64 = ZipUtil.recursionExportZipToBase64(list);
/** 上传到本地 **/
File file = this.generateZip(base64, newZipName);
log.info("file文件: " + file);
}
/**
* 将base64转为file文件 压缩包专用
*
* @param base64
* @param fileName
* @return
*/
public File generateZip(String base64, String fileName) {
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
File file = null;
try {
byte[] bytes = cn.hutool.core.codec.Base64.decode(base64);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
bis = new BufferedInputStream(byteArrayInputStream);
File zipFile = new File(FileUtil.DOWNLOAD_ZIP_FILE_PATH);
log.info("创建资源zipFile:" + zipFile.getPath());
if (!zipFile.exists()) {
zipFile.mkdir();
}
String trueFilePath = FileUtil.getZipFilePath(fileName);
file = new File(trueFilePath);
File path = file.getParentFile();
if (!path.exists()) {
path.mkdir();
}
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
byte[] buffer = new byte[1024];
int length = bis.read(buffer);
while (length != -1) {
bos.write(buffer, 0, length);
length = bis.read(buffer);
}
bos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bis.close();
fos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
}
package com.stream.core.util;
import com.alibaba.excel.util.CollectionUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* 打包下载
* @author WYG
*
*/
@Slf4j
public class ZipUtil {
@Data
public static class ZipPo{
/**
* 文件名/压缩包名
*/
private String name;
/**
* 附件路径,为压缩包时该字段为null
*/
private String path;
/**
* 递归的文件,当为文件时改字段为null
*/
private List<ZipPo> fileList;
public ZipPo(String name, String path) {
this.name = name;
this.path = path;
}
public ZipPo(String name, List<ZipPo> fileList) {
this.name = name;
this.fileList = fileList;
}
}
/**
* 将zip压缩包转为base64 大压缩包里面嵌套小压缩包
* @param fileList
*/
public static String recursionExportZipToBase64(List<ZipPo> fileList){
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try (
//把流输出到byteArrayOutputStream里面
ZipOutputStream zos1=new ZipOutputStream(byteArrayOutputStream, StandardCharsets.UTF_8);
){
recursionPackage(zos1,fileList);
zos1.close();
return Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
}catch (Exception e){
log.error("文件下载失败",e);
}
return null;
}
private static void recursionPackage(ZipOutputStream zos, List<ZipPo> fileList) {
//先初始化一个,每次使用的时候reset,防止内存溢出
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (ZipPo zipPo : fileList) {
zos.setMethod(ZipOutputStream.DEFLATED);
try {
zos.putNextEntry(new ZipEntry(zipPo.getName()));
} catch (Exception e) {
log.error(e.getMessage(),e);
}
//判断对象是附件还是压缩包
if(CollectionUtils.isEmpty(zipPo.getFileList())){ //附件
//获取附件并写到压缩包中
log.info("文件名称:" + zipPo.getName() + " 文件地址:" + zipPo.getPath());
if (StringUtil.isNotEmpty(zipPo.getPath())) {
try (InputStream is = new FileInputStream(zipPo.getPath())){
int b;
byte[] buffer = new byte[1024];
while ((b=is.read(buffer))!=-1){
zos.write(buffer,0,b);
}
zos.flush();
}catch (Exception e){
log.error("文件输出异常",e);
}
}
}else {//压缩包
//重置一下内存流
byteArrayOutputStream.reset();
try (
//把压缩包,往byteArrayOutputStream里面输出
ZipOutputStream zos2=new ZipOutputStream(byteArrayOutputStream, StandardCharsets.UTF_8);
){
//递归压缩
recursionPackage(zos2,zipPo.getFileList());
}catch (Exception e){
log.error("内部打包异常",e);
}
//把byteArrayOutputStream写到压缩包中,这个地方需要特别注意一下,不能把这两个写在上面的try里面,因为zos2关闭的时候会影响到zos,导致流的关闭顺序出错,压缩包文件被损坏,感谢评论区大佬的指正
try {
zos.write(byteArrayOutputStream.toByteArray());
zos.flush();
} catch (IOException e) {
log.error("内部打包异常",e);
}
}
}
}
/**
* 将文件打成压缩包操作
* @param fileList
* @throws IOException
*/
public static void zipFile(List<File> fileList, String zipPath) {
// 获取文件压缩包输出流
try (
OutputStream outputStream = new FileOutputStream(zipPath);
CheckedOutputStream checkedOutputStream = new CheckedOutputStream(outputStream,new Adler32());
ZipOutputStream zipOut = new ZipOutputStream(checkedOutputStream)){
for (File file : fileList) {
// 获取文件输入流
InputStream fileIn = new FileInputStream(file);
// 使用 common.io中的IOUtils获取文件字节数组
byte[] bytes = IOUtils.toByteArray(fileIn);
// 写入数据并刷新
zipOut.putNextEntry(new ZipEntry(file.getName()));
zipOut.write(bytes,0,bytes.length);
zipOut.flush();
fileIn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.stream.core.util;
import lombok.extern.slf4j.Slf4j;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* 文件读取工具类
*/
@Slf4j
public class FileUtil {
//系统文件分隔符
public static final String SEPARATOR = File.separator;
//文件下载路径这里是绝对路径,使用nas共享
public static final String DOWNLOAD_FILE_PATH = "/CWGXLOCAL/downloadFile";
//文件下载路径这里是绝对路径,使用nas共享
public static final String DOWNLOAD_ZIP_FILE_PATH = "/CWGXLOCAL/downloadZipFile";
/**
* 得到资源路径
*
* @return {@link String}
*/
public static String getResourcesPath() {
return System.getProperty("user.dir") + "/storageFile/" + DateUtils.getCurrentSimpDate();
}
/**
* 下载zip文件的全路径名
*
* @param fileName 文件名
* @return {@link String}
*/
public static String getZipFilePath(String fileName) {
File zipFile = new File(FileUtil.DOWNLOAD_ZIP_FILE_PATH);
log.info("创建资源zipFile:" + zipFile.getPath());
log.info("创建资源!zipFile.exists():" + !zipFile.exists());
if (!zipFile.exists()) {
zipFile.mkdir();
}
return DOWNLOAD_ZIP_FILE_PATH + SEPARATOR + fileName;
}
/**
* 从影像服务器获取下载地址 下载到本地nas
* @param fileName 附件名称
* @param downloadUrl 影像下载地址
* @return
* @throws IOException
*/
public static File downloadFilePath(String fileName, String downloadUrl) {
File file = null;
try {
URL url = new URL(downloadUrl);
File newFile = new File(fileName);
Path path = newFile.toPath();
Files.copy(url.openStream(), path);
file = path.toFile();
}catch (Exception e){
e.printStackTrace();
}
return file;
}
}