diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/exception/ZipException.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/exception/ZipException.java new file mode 100644 index 0000000..53d45cc --- /dev/null +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/exception/ZipException.java @@ -0,0 +1,22 @@ +package com.czcb.scfs.api.core.exception; + +/** + * @since 2.0.0 + */ +public class ZipException extends RuntimeException { + public ZipException() { + super(); + } + + public ZipException(String message) { + super(message); + } + + public ZipException(String message, Throwable cause) { + super(message, cause); + } + + public ZipException(Throwable cause) { + super(cause); + } +} diff --git a/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Compression.java b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Compression.java new file mode 100644 index 0000000..9dfd610 --- /dev/null +++ b/scfs-api-core/src/main/java/com/czcb/scfs/api/core/util/Compression.java @@ -0,0 +1,127 @@ +package com.czcb.scfs.api.core.util; + +import com.czcb.scfs.api.core.exception.ZipException; + +import java.io.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +/** + * 压缩文件 + */ +public final class Compression { + private Compression() { + } + + /** + * 压缩 + */ + public static byte[] zip(byte[] data, String filename) { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("待压缩数据不能为空"); + } + + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) { + ZipEntry zipEntry = new ZipEntry(filename); + zipEntry.setSize(data.length); + zipOutputStream.putNextEntry(zipEntry); + zipOutputStream.write(data); + zipOutputStream.closeEntry(); + return byteArrayOutputStream.toByteArray(); + } catch (Exception ex) { + throw new ZipException(String.format("压缩文件异常: %s", filename)); + } + } + + /** + * 解压 + */ + public static byte[] unzip(byte[] data) { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("待压缩数据不能为空"); + } + + try (ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(data)); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + while (zipInputStream.getNextEntry() != null) { + byte[] buffer = new byte[2048]; + int num; + + while ((num = zipInputStream.read(buffer, 0, buffer.length)) != -1) { + byteArrayOutputStream.write(buffer, 0, num); + } + } + + return byteArrayOutputStream.toByteArray(); + } catch (Exception ex) { + throw new ZipException("解压文件异常", ex); + } + } + + /** + * 1. 先进行zip压缩 + * 2. base64编码 + */ + public static String zipAndEncode(byte[] data, String fileName) { + if (data == null || data.length == 0) { + throw new IllegalArgumentException("待压缩数据不能为空"); + } + + return Base64.encodeStr(zip(data, fileName)); + } + + /** + * 1. base64解码 + * 2. 解压缩 + */ + public static byte[] decodeAndUnzip(String data) { + if (data == null || data.isEmpty()) { + throw new IllegalArgumentException("数据不能为空"); + } + + return unzip(Base64.decode(data)); + } + + /** + * 1. 先进行zip压缩 + * 2. base64编码 + */ + public static String zipAndEncodeFromStream(InputStream in, String filename) { + try { + // 文件流的长度是可预见的,此时直接读取效率更高 + final int available = in.available(); + final byte[] data = new byte[available]; + final int readLength = in.read(data); + if (readLength != available) { + throw new IOException(String.format("File length is [%s] but read [%s]!", available, readLength)); + } + + return zipAndEncode(data, filename); + } catch (IOException e) { + throw new ZipException(String.format("压缩文件异常: %s", filename)); + } + } + + public static String zipAndEncodeFromFile(String filepath) { + if (filepath == null || filepath.isEmpty()) { + throw new IllegalArgumentException("filepath 不能为空"); + } + + File tmpFile = new File(filepath); + try (FileInputStream inputStream = new FileInputStream(tmpFile)) { + return zipAndEncodeFromStream(inputStream, tmpFile.getName()); + } catch (IOException e) { + throw new ZipException(String.format("压缩文件异常: %s", filepath), e); + } + } + + public static String zipAndEncodeFromFile(File file) { + if (file == null) { + throw new IllegalArgumentException("file 不能为 null"); + } + + return zipAndEncodeFromFile(file.getAbsolutePath()); + } +} diff --git a/scfs-api-core/src/test/java/com/czcb/scfs/api/core/util/CompressionTest.java b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/util/CompressionTest.java new file mode 100644 index 0000000..9bb7f47 --- /dev/null +++ b/scfs-api-core/src/test/java/com/czcb/scfs/api/core/util/CompressionTest.java @@ -0,0 +1,112 @@ +package com.czcb.scfs.api.core.util; + +import com.czcb.scfs.api.core.exception.ZipException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +class CompressionTest { + @Test + void zip() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zip(null, null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zip(new byte[0], null); + }); + + Assertions.assertThrows(ZipException.class, () -> { + Compression.zip("test".getBytes(StandardCharsets.UTF_8), null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.unzip(null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.unzip(new byte[0]); + }); + + String text = "l1231344"; + String filename = "a.txt"; + + byte[] data = Compression.zip(text.getBytes(StandardCharsets.UTF_8), filename); + + String unzipText = new String(Compression.unzip(data), StandardCharsets.UTF_8); + Assertions.assertEquals(text, unzipText); + } + + @Test + void decode() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zipAndEncode(null, null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zipAndEncode(new byte[]{}, null); + }); + + String text = "l1231344"; + String filename = "a.txt"; + + String data = Compression.zipAndEncode(text.getBytes(StandardCharsets.UTF_8), filename); + + String unzipText = new String(Compression.decodeAndUnzip(data), StandardCharsets.UTF_8); + Assertions.assertEquals(text, unzipText); + } + + @Test + void decodeStream() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.decodeAndUnzip(null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.decodeAndUnzip(""); + }); + + String text = "l1231344"; + String filename = "a.txt"; + ByteArrayInputStream inputStream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + String data = Compression.zipAndEncodeFromStream(inputStream, filename); + + String unzipText = new String(Compression.decodeAndUnzip(data), StandardCharsets.UTF_8); + Assertions.assertEquals(text, unzipText); + } + + @Test + void decodeFile() throws IOException { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zipAndEncodeFromFile(""); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zipAndEncodeFromFile((String) null); + }); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + Compression.zipAndEncodeFromFile((File) null); + }); + + String text = "l1231344"; + File file = Files.createTempFile("decodeFile-", "").toFile(); + try { + FileOutputStream outputStream = new FileOutputStream(file); + outputStream.write(text.getBytes(StandardCharsets.UTF_8)); + outputStream.close(); + + String data = Compression.zipAndEncodeFromFile(file); + String unzipText = new String(Compression.decodeAndUnzip(data), StandardCharsets.UTF_8); + Assertions.assertEquals(text, unzipText); + } finally { + file.delete(); + } + } +} \ No newline at end of file