目录结构
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
<version>2.3.2.RELEASEversion>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.75version>
<scope>compilescope>
dependency>
import com.alibaba.fastjson.JSONObject;
import org.springframework.util.StringUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* 压缩文件工具。
* 不生成中间过渡文件,直接操作压缩包。
* 仅支持一层文件【非文件夹,非压缩包】操作。
*
* 注意:
* 不适用于【高并发场景】。
*
* @author zhangsan coder
* @since 2022-09-4 14:52
**/
public class ZipUtil {
/**
* 压缩 zip 到 outputStream 位置。
* 压缩 zip 文件。
*
* 注意:inputStreamList 与 zipFileNames 长度必须一一对应,文件数据与名称一对一的对应关系。
*
* 如:
* inputStreamList[0] -> zipFileNames[0]。
* inputStreamList[1] -> zipFileNames[1]。
* inputStreamList[2] -> zipFileNames[2]。
*
* @param inputStreamList 被压缩的列表数据。【String形式】
* @param zipFileNames zip 中的每个文件的名称。【长度必须严格与 inputStreamList 同长度】
* @param outputStream 压缩包输出位置。
*/
public static void toZip(List<InputStream> inputStreamList, List<String> zipFileNames, OutputStream outputStream) throws IOException {
if (inputStreamList.size() <= 0) {
return;
}
if (zipFileNames.size() <= 0) {
return;
}
// 设置压缩流:直接写入response,实现边压缩边下载
ZipOutputStream zipos = null;
try {
zipos = new ZipOutputStream(new BufferedOutputStream(outputStream));
zipos.setMethod(ZipOutputStream.DEFLATED); //设置压缩方法
} catch (Exception e) {
return;
}
// 循环将文件写入压缩流
DataOutputStream os = null;
for (int i = 0; i < inputStreamList.size(); i++) {
zipos.putNextEntry(new ZipEntry(StringUtils.isEmpty(zipFileNames.get(i)) ? "解压的数据" + i : zipFileNames.get(i)));
os = new DataOutputStream(zipos);
byte[] b = new byte[4 * 1024];
int length = 0;
while ((length = inputStreamList.get(i).read(b)) != -1) {
os.write(b, 0, length);
}
inputStreamList.get(i).close();
zipos.closeEntry();
}
// 关闭流
try {
if (os != null) {
os.flush();
os.close();
}
zipos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 解压 zip 并 获取解压后的数据。【单个 zip】
* 注意:
* 得确保 zip 文件中的数据为 JSON 数据;
* 且 JSON 数据必须与 toClass 属性字段保持一致!
* 属性取值范围只能在 toClass 属性名内,超过范围则报错!
* 仅支持一层解压。
*
* 如:
* 有字段:a、b、c、d。
* 转换成功:a、b。 a、b、c。 a、c。 a、b、c、d。
* 转换失败:a、e。 a、b、f。
*
* @param zipInputStream 压缩流。
* @param toClass 转换的类型。
* @return 解压后的文件数据。
*/
// @SneakyThrows
public static <T> List<T> unZipForJsonToList(ZipInputStream zipInputStream, Class<T> toClass) throws IOException, NullPointerException {
if (toClass == null) {
throw new NullPointerException("提取的对象不能为空!");
}
if (zipInputStream == null) {
throw new NullPointerException("压缩流不能为空!");
}
List<T> list = new ArrayList<>();
Map<String, String> zipFile = getZipFile(zipInputStream);
//map 的entry遍历方法
for (Map.Entry<String, String> en : zipFile.entrySet()) {
// System.out.println(en.getKey() + " ==》 " + en.getValue());
T t = JSONObject.parseObject(en.getValue(), toClass);
list.add(t);
}
if (list.size() <= 0) {
throw new NullPointerException("zip 提取数据失败!");
}
return list;
}
/**
* 解压 zip 并 获取解压后的数据。【单个 zip】
* 注意:
* 得确保 zip 文件中的数据为 JSON 数据;
* 且 JSON 数据必须与 toClass 属性字段保持一致!
* 属性取值范围只能在 toClass 属性名内,超过范围则报错!
* 仅支持一层解压。
*
* 如:
* 有字段:a、b、c、d。
* 转换成功:a、b。 a、b、c。 a、c。 a、b、c、d。
* 转换失败:a、e。 a、b、f。
*
* @param zipInputStream 压缩流。
* @param toClass 转换的类型。
* @return 解压后的文件数据。
*/
// @SneakyThrows
public static <T> Map<String, T> unZipForJsonToMap(ZipInputStream zipInputStream, Class<T> toClass) throws IOException, NullPointerException {
if (toClass == null) {
throw new NullPointerException("提取的对象不能为空!");
}
if (zipInputStream == null) {
throw new NullPointerException("压缩流不能为空!");
}
Map<String, String> zipFile = getZipFile(zipInputStream);
Map<String, T> returnMap = new ConcurrentHashMap<>();
//map 的entry遍历方法
for (Map.Entry<String, String> en : zipFile.entrySet()) {
// System.out.println(en.getKey() + " ==》 " + en.getValue());
T t = JSONObject.parseObject(en.getValue(), toClass);
returnMap.put(en.getKey(), t);
}
if (returnMap.size() <= 0) {
throw new NullPointerException("zip 提取数据失败!");
}
return returnMap;
}
/**
* 根据入参,返回提取到的文件字符串形式数据。
*
* @param srcZip 源zip。
* @return 返回提取到的文件数据内容【字符串形式】,若没有获取到内容,则返回 null 。
* @throws IOException
*/
public static Map<String, String> getZipFile(ZipInputStream srcZip) throws IOException {
//读取一个目录
ZipEntry nextEntry = null;
// 压缩包内的文件数据【字符串形式】。
Map<String, String> stringMap = new ConcurrentHashMap<>();
StringBuilder jsonString = new StringBuilder();
//不为空,则进入循环。
while ((nextEntry = srcZip.getNextEntry()) != null) {
String name = nextEntry.getName();
// System.out.println("name = " + name);
// for (String fileName : fileNames) {
// if (!StringUtils.isEmpty(name) && name.equalsIgnoreCase(fileName)) {
// System.out.println("压缩包中存在当前文件名:" + fileName);
// System.out.println();
// 提取文件数据。
try (
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream)
) {
int n;
byte[] bytes = new byte[4 * 1024];
// byte[] bytes = new byte[1];
while ((n = srcZip.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, n);
// 获取文件数据。
jsonString.append(new String(bytes, 0, n));
// System.out.print(jsonString);
}
stringMap.put(name, jsonString.toString());
jsonString = new StringBuilder();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭当前文件。
srcZip.closeEntry();
}
// System.out.println();
// System.out.println("================================================================================");
// System.out.println();
}
return stringMap;
}
// public static void main(String[] args) throws IOException {
private static void main(String[] args) throws IOException {
// List routes = unZipForJson(
// new ZipInputStream(new FileInputStream(new File("C:\\\\Users\\\\zhangsan\\\\Desktop\\\\测速.zip")))
// , Route.class
// );
// System.out.println("============================ 转换的对象列表 ==========================");
// routes.forEach(System.out::println);
// System.out.println("===================================================================");
}
}
原因:传输流其中一端突然断开关闭导致的。
注: 在Java中,没有具体的BrokenPipeException。 将此类错误包含在另一个异常,例如java.io.IOException:Broken pipe
问题分析:
参考文献
地址:参考文件