代码——将zip压缩文件按指定目录层级拆分成子zip文件
1228
2022-01-29
将zip压缩文件按指定目录层级拆分成子zip文件的工具方法,主要逻辑是读取zip压缩文件、分析目录结构并进行分组、按分组重新生成zip压缩文件
示例如下:
如A.zip文件,内部结构为:
– 文件夹A1
– 文件夹A1 – 文件夹B1
– 文件夹A1 – 文件夹B1 – 文件1.xlsx
– 文件夹A1 – 文件夹B1 – 文件2.docx
– 文件夹A1 – 文件夹B1 – 文件夹C1
– 文件夹A1 – 文件夹B1 – 文件夹C1 – 文件3.mp4
– 文件夹A1 – 文件夹B1 – 文件夹C1 – 文件4.wmv
– 文件夹A2
– 文件夹A2 – 文件夹B2
– 文件夹A2 – 文件夹B2 – 文件5.txt
– 文件夹A2 – 文件夹B2 – 文件夹C2
– 文件夹A2 – 文件夹B2 – 文件夹C2 – 文件6.jpeg
调用方法splitZipFileAndCreateChildZipFileGroupByFolder(zipFileInputStream, 2)后,会在指定目录下生成两个zip文件,分别是
文件夹B1.zip,内部结构为:
– 文件1.xlsx
– 文件2.docx
– 文件夹C1
– 文件夹C1 – 文件3.mp4
– 文件夹C1 – 文件4.wmv
文件夹B2.zip,内部结构为:
– 文件5.txt
– 文件夹C2
– 文件夹C2 – 文件6.jpeg
代码如下:
import com.nd.edu.base.common.ExceptionDefinition;
import com.nd.gaea.core.utils.DateUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
@Slf4j
public class ZipFileUtil {
private static final String FOLDER_SEPARATOR = "/";
/**
* 拆分zip内按文件夹分组并重新生成子zip文件
* @param zipFileInputStream zip文件输入流
* @param level 要拆分的层级维度 1-表示根目录下的一级目录
*/
public static void splitZipFileAndCreateChildZipFileGroupByFolder(
InputStream zipFileInputStream, int level) {
try {
byte[] bytes = IOUtils.toByteArray(zipFileInputStream);
IOUtils.closeQuietly(zipFileInputStream);
Map<String, Map<String, byte[]>> folderFileNameContentMap =
FileUtil.readZipFileByteArrayGroupByFolder(bytes, level);
if (folderFileNameContentMap.isEmpty()) {
return;
}
for (String folderName : folderFileNameContentMap.keySet()) {
byte[] childZipByteArr =
createZipFileByteArray(folderName, folderFileNameContentMap.get(folderName));
createAndWriteFile("temp", folderName, "zip", childZipByteArr);
}
} catch (Exception ex) {
log.error("splitZipAndCreateChildZipGroupByFolder:{}", ex.getMessage());
}
}
/**
* 从zip文件中按指定层级的目录对文件进行分组,获取文件字节数组
*
* @param contents zip文件字节数组
* @param level 要按几级目录分组 1-表示根目录下的一级目录
* @return 分组目录name->文件name和字节数组的map
*/
public static Map<String, Map<String, byte[]>> readZipFileByteArrayGroupByFolder(
byte[] contents, int level) {
Map<String, Map<String, byte[]>> folderZipFileMap = new HashMap<>(100);
try {
ZipInputStream zipInput =
new ZipInputStream(new ByteArrayInputStream(contents), Charset.forName("GBK"));
ZipEntry zip = null;
while ((zip = zipInput.getNextEntry()) != null) {
// zip.getName()如果是文件夹,返回的是XXX/,末尾有个/
String[] pathArr = zip.getName().split(FOLDER_SEPARATOR);
if (pathArr.length < level) {
// 忽略指定层级之上的目录和文件
continue;
}
String baseFolder = pathArr[level - 1];
if (zip.isDirectory()) {
if (pathArr.length == level) {
// 说明层级是要进行分组的文件夹
folderZipFileMap.put(baseFolder, new HashMap<>(10));
} else {
// 说明是子文件夹,需要将路径处理去掉指定层级前的部分
String[] newPathArr = new String[pathArr.length - level];
System.arraycopy(pathArr, level, newPathArr, 0, pathArr.length - level);
String newFolderPath = StringUtils.join(newPathArr);
folderZipFileMap.get(baseFolder).put(newFolderPath, readContent(zipInput));
}
} else {
if (pathArr.length > level) {
// 仅处理分组层级下的文件和子目录里的文件
String[] newPathArr = new String[pathArr.length - level];
System.arraycopy(pathArr, level, newPathArr, 0, pathArr.length - level);
String fileName = StringUtils.join(newPathArr, FOLDER_SEPARATOR);
folderZipFileMap.get(baseFolder).put(fileName, readContent(zipInput));
}
}
}
zipInput.closeEntry();
return folderZipFileMap;
} catch (IOException e) {
throw ExceptionDefinition.ZIP_READ_FAIL.create();
}
}
/**
* 生成zip压缩文件字节数组
*
* @param zipFileName zip文件名
* @param innerFileMap 压缩包内要写入的文件 文件name->文件字节数组
*/
public static byte[] createZipFileByteArray(
String zipFileName, Map<String, byte[]> innerFileMap) {
byte[] fileByteArr = null;
try {
try (ByteArrayOutputStream zipArrOutputStream = new ByteArrayOutputStream()) {
try (ZipOutputStream zipOutputStream = new ZipOutputStream(zipArrOutputStream)) {
// 先生成好目录结构
for (String fileName : innerFileMap.keySet()) {
ByteArrayInputStream fileInputStream =
new ByteArrayInputStream(innerFileMap.get(fileName));
if (fileName.contains(".")) {
continue;
}
// 如果是文件夹,需要在末尾加/
zipOutputStream.putNextEntry(new ZipEntry(fileName + "/"));
zipOutputStream.write(IOUtils.toByteArray(fileInputStream));
}
for (String fileName : innerFileMap.keySet()) {
ByteArrayInputStream fileInputStream =
new ByteArrayInputStream(innerFileMap.get(fileName));
if (!fileName.contains(".")) {
continue;
}
zipOutputStream.putNextEntry(new ZipEntry(fileName));
zipOutputStream.write(IOUtils.toByteArray(fileInputStream));
}
}
fileByteArr = zipArrOutputStream.toByteArray();
}
} catch (Exception ex) {
log.error("生成zip压缩文件{}失败:{}", zipFileName, ex.getMessage());
}
return fileByteArr;
}
/**
* 读取zip文件并返回字节数组
*
* @param zipInput ZipInputStream
* @return byte[]
* @throws IOException
*/
private static byte[] readContent(ZipInputStream zipInput) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count = -1;
while ((count = zipInput.read(buffer)) != -1) {
out.write(buffer, 0, count);
}
out.close();
return out.toByteArray();
}
}