Compare commits
12 Commits
47f1c9520e
...
v0.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 279289fb22 | |||
|
|
b56f4e8969 | ||
| 913b9b5328 | |||
|
|
56dec33c94 | ||
| 88b1fd6e02 | |||
|
|
a24b1855aa | ||
| 1f13cdd7bc | |||
|
|
f38975f633 | ||
| 0b078278f0 | |||
|
|
7d213c54af | ||
| c0b2ae11a7 | |||
|
|
afcc902cbf |
111
.gitea/workflows/ci.yml
Normal file
111
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
name: CI/CD
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-deploy:
|
||||||
|
runs-on: act_runner_java
|
||||||
|
if: ${{ github.event.pull_request.merged == true }}
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/jvm/java-21-openjdk
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
run: |
|
||||||
|
git clone ${{ github.server_url }}/${{ github.repository }}.git .
|
||||||
|
git checkout ${{ github.sha }}
|
||||||
|
- name: Set up environment
|
||||||
|
run: |
|
||||||
|
echo "PR #${{ github.event.number }} merged into master"
|
||||||
|
echo "Source branch: ${{ github.event.pull_request.head.ref }}"
|
||||||
|
echo "Target branch: ${{ github.event.pull_request.base.ref }}"
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
echo "Running test suite..."
|
||||||
|
- name: Build project
|
||||||
|
run: |
|
||||||
|
mvn -B -DskipTests clean package source:jar javadoc:jar
|
||||||
|
- name: Deploy to Nexus
|
||||||
|
if: success()
|
||||||
|
run: |
|
||||||
|
if [ -z "${{ secrets.NEXUS_USERNAME }}" ] || [ -z "${{ secrets.NEXUS_PASSWORD }}" ]; then
|
||||||
|
echo "Missing secrets.NEXUS_USERNAME or secrets.NEXUS_PASSWORD"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p ~/.m2
|
||||||
|
cat > ~/.m2/settings.xml <<EOF
|
||||||
|
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||||
|
<servers>
|
||||||
|
<server>
|
||||||
|
<id>timi-nexus</id>
|
||||||
|
<username>${{ secrets.NEXUS_USERNAME }}</username>
|
||||||
|
<password>${{ secrets.NEXUS_PASSWORD }}</password>
|
||||||
|
</server>
|
||||||
|
</servers>
|
||||||
|
</settings>
|
||||||
|
EOF
|
||||||
|
version=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version)
|
||||||
|
artifact_id=$(mvn -q -DforceStdout help:evaluate -Dexpression=project.artifactId)
|
||||||
|
main_jar="target/${artifact_id}-${version}.jar"
|
||||||
|
sources_jar="target/${artifact_id}-${version}-sources.jar"
|
||||||
|
javadoc_jar="target/${artifact_id}-${version}-javadoc.jar"
|
||||||
|
if [ ! -f "$main_jar" ] || [ ! -f "$sources_jar" ] || [ ! -f "$javadoc_jar" ]; then
|
||||||
|
echo "Missing build artifacts in target"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mvn -B deploy:deploy-file \
|
||||||
|
-Dfile="$main_jar" \
|
||||||
|
-Dsources="$sources_jar" \
|
||||||
|
-Djavadoc="$javadoc_jar" \
|
||||||
|
-DpomFile="./pom.xml" \
|
||||||
|
-Durl="https://nexus.imyeyu.com/repository/maven-releases/" \
|
||||||
|
-DrepositoryId="timi-nexus" \
|
||||||
|
-Dhttps.protocols=TLSv1.2 \
|
||||||
|
-Djdk.tls.client.protocols=TLSv1.2
|
||||||
|
- name: Create release
|
||||||
|
if: ${{ success() && startsWith(github.event.pull_request.title, 'v') }}
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.RUNNER_TOKEN }}
|
||||||
|
GITEA_SERVER_URL: ${{ github.server_url }}
|
||||||
|
GITEA_REPOSITORY: ${{ github.repository }}
|
||||||
|
RELEASE_TAG: ${{ github.event.pull_request.title }}
|
||||||
|
RELEASE_TARGET: ${{ github.sha }}
|
||||||
|
run: |
|
||||||
|
if [ -z "$GITEA_TOKEN" ]; then
|
||||||
|
echo "Missing secrets.RUNNER_TOKEN"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
api_url="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY/releases"
|
||||||
|
payload=$(cat <<EOF
|
||||||
|
{
|
||||||
|
"tag_name": "$RELEASE_TAG",
|
||||||
|
"name": "$RELEASE_TAG",
|
||||||
|
"target_commitish": "$RELEASE_TARGET",
|
||||||
|
"draft": false,
|
||||||
|
"prerelease": false
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
response=$(curl -sS -X POST "$api_url" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$payload")
|
||||||
|
release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -n 1 | grep -o '[0-9]*')
|
||||||
|
if [ -z "$release_id" ] || echo "$response" | grep -q '"message"'; then
|
||||||
|
echo "Create release failed: $response"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Release created: id=$release_id"
|
||||||
|
for asset_path in target/*.jar; do
|
||||||
|
asset_name=$(basename "$asset_path")
|
||||||
|
curl -sS -X POST "$api_url/$release_id/assets?name=$asset_name" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"$asset_path"
|
||||||
|
done
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,6 @@
|
|||||||
|
/testOut*
|
||||||
|
/AGENTS.md
|
||||||
|
|
||||||
target/
|
target/
|
||||||
!.mvn/wrapper/maven-wrapper.jar
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
!**/src/main/**/target/
|
!**/src/main/**/target/
|
||||||
@@ -36,5 +39,3 @@ build/
|
|||||||
|
|
||||||
### Mac OS ###
|
### Mac OS ###
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
/testOut*
|
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -1,3 +1,19 @@
|
|||||||
# timi-compress
|
# timi-compress
|
||||||
|
|
||||||
Java 压缩、解压缩工具
|
Java 压缩、解压缩工具
|
||||||
|
|
||||||
|
## 调用方式
|
||||||
|
|
||||||
|
压缩:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ZipCompressor.of("testSrc").toFile("testOut/test.zip");
|
||||||
|
TarCompressor.of(file).toStream(outputStream);
|
||||||
|
```
|
||||||
|
|
||||||
|
解压:
|
||||||
|
|
||||||
|
```java
|
||||||
|
ZipDecompressor.of("testOut/test.zip").to("testOutDe");
|
||||||
|
GZipDecompressor.of(inputStream).to("testOutDe");
|
||||||
|
```
|
||||||
|
|||||||
78
pom.xml
78
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.imyeyu.compress</groupId>
|
<groupId>com.imyeyu.compress</groupId>
|
||||||
<artifactId>timi-compress</artifactId>
|
<artifactId>timi-compress</artifactId>
|
||||||
<version>0.0.1</version>
|
<version>0.0.3</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<maven.test.skip>true</maven.test.skip>
|
<maven.test.skip>true</maven.test.skip>
|
||||||
@@ -19,32 +19,94 @@
|
|||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-deploy-plugin</artifactId>
|
||||||
<version>3.11.0</version>
|
<version>3.1.3</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok-maven-plugin</artifactId>
|
||||||
|
<version>1.18.20.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>21</source>
|
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
|
||||||
<target>21</target>
|
<outputDirectory>${project.build.directory}/delombok</outputDirectory>
|
||||||
|
<addOutputDirectory>false</addOutputDirectory>
|
||||||
<encoding>UTF-8</encoding>
|
<encoding>UTF-8</encoding>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>generate-sources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>delombok</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.36</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.11.2</version>
|
||||||
|
<configuration>
|
||||||
|
<sourcepath>${project.build.directory}/delombok</sourcepath>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
<docencoding>UTF-8</docencoding>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>timi_nexus</id>
|
||||||
|
<url>https://nexus.imyeyu.com/repository/maven-releases/</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>timi_nexus</id>
|
||||||
|
<url>https://nexus.imyeyu.com/repository/maven-public/</url>
|
||||||
|
<releases>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.imyeyu.io</groupId>
|
<groupId>com.imyeyu.io</groupId>
|
||||||
<artifactId>timi-io</artifactId>
|
<artifactId>timi-io</artifactId>
|
||||||
<version>0.0.1</version>
|
<version>0.0.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-compress</artifactId>
|
<artifactId>commons-compress</artifactId>
|
||||||
<version>1.26.1</version>
|
<version>1.28.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.tukaani</groupId>
|
<groupId>org.tukaani</groupId>
|
||||||
<artifactId>xz</artifactId>
|
<artifactId>xz</artifactId>
|
||||||
<version>1.9</version>
|
<version>1.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.40</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package com.imyeyu.compress;
|
|
||||||
|
|
||||||
import com.imyeyu.java.bean.CallbackArg;
|
|
||||||
import com.imyeyu.utils.OS;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 抽象解压缩执行器
|
|
||||||
*
|
|
||||||
* @author 夜雨
|
|
||||||
* @since 2024-06-30 18:09
|
|
||||||
*/
|
|
||||||
public abstract class AbstractCompressor implements OS.FileSystem {
|
|
||||||
|
|
||||||
/** 操作文件回调 */
|
|
||||||
protected CallbackArg<File> fileCallback;
|
|
||||||
|
|
||||||
/** 进度回调 */
|
|
||||||
protected CallbackArg<Double> progressCallback;
|
|
||||||
|
|
||||||
/** 中止 */
|
|
||||||
protected boolean isInterrupt = false;
|
|
||||||
|
|
||||||
/** 暂停 */
|
|
||||||
protected boolean isPause = false;
|
|
||||||
|
|
||||||
/** 暂停锁 */
|
|
||||||
protected final Object pauseLock = new Object();
|
|
||||||
|
|
||||||
/** 暂停 */
|
|
||||||
public void pause() {
|
|
||||||
isPause = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 开始 */
|
|
||||||
public void start() {
|
|
||||||
isPause = false;
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 中止 */
|
|
||||||
public void interrupt() {
|
|
||||||
isInterrupt = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置操作文件回调。正在压缩或解压某文件时触发
|
|
||||||
*
|
|
||||||
* @param fileCallback 回调接口
|
|
||||||
*/
|
|
||||||
public void setFileCallback(CallbackArg<File> fileCallback) {
|
|
||||||
this.fileCallback = fileCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置进度回调
|
|
||||||
*
|
|
||||||
* @param progressCallback 回调接口
|
|
||||||
*/
|
|
||||||
public void setProgressCallback(CallbackArg<Double> progressCallback) {
|
|
||||||
this.progressCallback = progressCallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
285
src/main/java/com/imyeyu/compress/AbstractRunner.java
Normal file
285
src/main/java/com/imyeyu/compress/AbstractRunner.java
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
package com.imyeyu.compress;
|
||||||
|
|
||||||
|
import com.imyeyu.java.bean.CallbackArg;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
import com.imyeyu.utils.OS;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.FilterOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象处理器
|
||||||
|
*
|
||||||
|
* @param <T> 处理器类型
|
||||||
|
* @author 夜雨
|
||||||
|
* @since 2024-06-30 18:09
|
||||||
|
*/
|
||||||
|
public abstract class AbstractRunner<T extends AbstractRunner<T>> implements OS.FileSystem {
|
||||||
|
|
||||||
|
/** 文件处理回调 */
|
||||||
|
protected CallbackArg<File> fileCallback;
|
||||||
|
|
||||||
|
/** 进度回调 */
|
||||||
|
protected CallbackArg<Double> progressCallback;
|
||||||
|
|
||||||
|
/** 中断标记 */
|
||||||
|
protected boolean isInterrupt = false;
|
||||||
|
|
||||||
|
/** 暂停标记 */
|
||||||
|
protected boolean isPause = false;
|
||||||
|
|
||||||
|
/** 暂停锁 */
|
||||||
|
protected final Object pauseLock = new Object();
|
||||||
|
|
||||||
|
/** 总字节数 */
|
||||||
|
private long progressTotalBytes = -1L;
|
||||||
|
|
||||||
|
/** 当前已处理字节数 */
|
||||||
|
private long progressBytes = 0L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文件处理回调
|
||||||
|
*
|
||||||
|
* @param fileCallback 文件处理回调
|
||||||
|
* @return 当前处理器
|
||||||
|
*/
|
||||||
|
public T setFileCallback(CallbackArg<File> fileCallback) {
|
||||||
|
this.fileCallback = fileCallback;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文件处理回调
|
||||||
|
*
|
||||||
|
* @param fileCallback 文件处理回调
|
||||||
|
* @return 当前处理器
|
||||||
|
*/
|
||||||
|
public T fileCallback(CallbackArg<File> fileCallback) {
|
||||||
|
return setFileCallback(fileCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置进度回调
|
||||||
|
*
|
||||||
|
* @param progressCallback 进度回调
|
||||||
|
* @return 当前处理器
|
||||||
|
*/
|
||||||
|
public T setProgressCallback(CallbackArg<Double> progressCallback) {
|
||||||
|
this.progressCallback = progressCallback;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置进度回调
|
||||||
|
*
|
||||||
|
* @param progressCallback 进度回调
|
||||||
|
* @return 当前处理器
|
||||||
|
*/
|
||||||
|
public T progressCallback(CallbackArg<Double> progressCallback) {
|
||||||
|
return setProgressCallback(progressCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 暂停任务 */
|
||||||
|
public void pause() {
|
||||||
|
isPause = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 恢复任务 */
|
||||||
|
public void start() {
|
||||||
|
isPause = false;
|
||||||
|
synchronized (pauseLock) {
|
||||||
|
pauseLock.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 中断任务 */
|
||||||
|
public void interrupt() {
|
||||||
|
isInterrupt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回当前处理器
|
||||||
|
*
|
||||||
|
* @return 当前处理器
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected final T self() {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化字节进度
|
||||||
|
*
|
||||||
|
* @param totalBytes 总字节数
|
||||||
|
*/
|
||||||
|
protected void initByteProgress(long totalBytes) {
|
||||||
|
progressBytes = 0L;
|
||||||
|
progressTotalBytes = totalBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置进度状态 */
|
||||||
|
protected void resetProgress() {
|
||||||
|
progressBytes = 0L;
|
||||||
|
progressTotalBytes = -1L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行暂停检查
|
||||||
|
*
|
||||||
|
* @throws InterruptedException 等待恢复时被中断
|
||||||
|
*/
|
||||||
|
protected void awaitIfPaused() throws InterruptedException {
|
||||||
|
if (!isPause) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (pauseLock) {
|
||||||
|
while (isPause) {
|
||||||
|
pauseLock.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否已经中断
|
||||||
|
*
|
||||||
|
* @throws IOException 操作已中断
|
||||||
|
*/
|
||||||
|
protected void ensureRunning() throws IOException {
|
||||||
|
if (isInterrupt) {
|
||||||
|
throw new IOException("操作已中断");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发文件回调
|
||||||
|
*
|
||||||
|
* @param file 当前处理的文件
|
||||||
|
*/
|
||||||
|
protected void handleFile(File file) {
|
||||||
|
if (fileCallback != null) {
|
||||||
|
fileCallback.handler(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发进度回调
|
||||||
|
*
|
||||||
|
* @param currentBytes 当前已处理字节数
|
||||||
|
* @param totalBytes 总字节数
|
||||||
|
*/
|
||||||
|
protected void handleProgress(long currentBytes, long totalBytes) {
|
||||||
|
if (progressCallback == null || totalBytes < 1L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double progress = Math.min(1D, currentBytes * 1D / totalBytes);
|
||||||
|
progressCallback.handler(progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加已处理字节数并回调
|
||||||
|
*
|
||||||
|
* @param bytes 本次新增字节数
|
||||||
|
*/
|
||||||
|
protected void handleTransferred(long bytes) {
|
||||||
|
if (bytes < 1L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressBytes += bytes;
|
||||||
|
handleProgress(progressBytes, progressTotalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将进度推进到完成态 */
|
||||||
|
protected void finishProgress() {
|
||||||
|
if (progressCallback != null) {
|
||||||
|
progressCallback.handler(1D);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制数据流并更新进度
|
||||||
|
*
|
||||||
|
* @param fromStream 输入流
|
||||||
|
* @param toStream 输出流
|
||||||
|
* @return 已复制字节数
|
||||||
|
* @throws Exception 复制失败
|
||||||
|
*/
|
||||||
|
protected long transfer(InputStream fromStream, OutputStream toStream) throws Exception {
|
||||||
|
return transfer(fromStream, toStream, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制数据流
|
||||||
|
*
|
||||||
|
* @param fromStream 输入流
|
||||||
|
* @param toStream 输出流
|
||||||
|
* @param countProgress 是否统计进度
|
||||||
|
* @return 已复制字节数
|
||||||
|
* @throws Exception 复制失败
|
||||||
|
*/
|
||||||
|
protected long transfer(InputStream fromStream, OutputStream toStream, boolean countProgress) throws Exception {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long total = 0L;
|
||||||
|
int length;
|
||||||
|
while ((length = fromStream.read(buffer)) != -1) {
|
||||||
|
awaitIfPaused();
|
||||||
|
ensureRunning();
|
||||||
|
toStream.write(buffer, 0, length);
|
||||||
|
total += length;
|
||||||
|
if (countProgress) {
|
||||||
|
handleTransferred(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个关闭时不关闭原始流的输入流包装
|
||||||
|
*
|
||||||
|
* @param inputStream 原始输入流
|
||||||
|
* @return 包装后的输入流
|
||||||
|
*/
|
||||||
|
protected InputStream nonClosing(InputStream inputStream) {
|
||||||
|
return new FilterInputStream(inputStream) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个关闭时不关闭原始流的输出流包装
|
||||||
|
*
|
||||||
|
* @param outputStream 原始输出流
|
||||||
|
* @return 包装后的输出流
|
||||||
|
*/
|
||||||
|
protected OutputStream nonClosing(OutputStream outputStream) {
|
||||||
|
return new FilterOutputStream(outputStream) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规范化归档条目名称
|
||||||
|
*
|
||||||
|
* @param entryName 条目名称
|
||||||
|
* @return 规范化后的名称
|
||||||
|
*/
|
||||||
|
protected String normalizeEntryName(String entryName) {
|
||||||
|
TimiException.required(entryName, "not found entryName");
|
||||||
|
String normalized = entryName.replace('\\', '/');
|
||||||
|
while (normalized.startsWith("/")) {
|
||||||
|
normalized = normalized.substring(1);
|
||||||
|
}
|
||||||
|
TimiException.required(normalized, "not found normalized entryName");
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ package com.imyeyu.compress;
|
|||||||
|
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
import com.imyeyu.java.ref.Ref;
|
import com.imyeyu.java.ref.Ref;
|
||||||
|
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
|
||||||
|
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
|
||||||
|
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -15,30 +19,26 @@ import java.io.InputStream;
|
|||||||
public enum CompressType {
|
public enum CompressType {
|
||||||
|
|
||||||
/** 7z */
|
/** 7z */
|
||||||
Z7(Z7Compressor.class, Z7Decompressor.class, -0x51),
|
Z7(Z7Compressor.class, Z7Decompressor.class),
|
||||||
|
|
||||||
/** Zip */
|
/** Zip */
|
||||||
ZIP(ZipCompressor.class, ZipDecompressor.class, 0x504B0304),
|
ZIP(ZipCompressor.class, ZipDecompressor.class),
|
||||||
|
|
||||||
/** Gzip */
|
/** Gzip */
|
||||||
GZIP(GZipCompressor.class, GZipDecompressor.class, -0x74F7F8),
|
GZIP(GZipCompressor.class, GZipDecompressor.class),
|
||||||
|
|
||||||
/** tar */
|
/** Tar */
|
||||||
TAR(TarCompressor.class, TarDecompressor.class, 0x776F7264);
|
TAR(TarCompressor.class, TarDecompressor.class);
|
||||||
|
|
||||||
/** 压缩类 */
|
/** 压缩器类型 */
|
||||||
final Class<? extends Compressor> compressorType;
|
final Class<? extends Compressor<?>> compressorType;
|
||||||
|
|
||||||
/** 解压类 */
|
/** 解压器类型 */
|
||||||
final Class<? extends Decompressor> decompressorType;
|
final Class<? extends Decompressor<?>> decompressorType;
|
||||||
|
|
||||||
/** 文件头标记 */
|
CompressType(Class<? extends Compressor<?>> compressorType, Class<? extends Decompressor<?>> decompressorType) {
|
||||||
final int headHex;
|
|
||||||
|
|
||||||
CompressType(Class<? extends Compressor> compressorType, Class<? extends Decompressor> decompressorType, int headHex) {
|
|
||||||
this.compressorType = compressorType;
|
this.compressorType = compressorType;
|
||||||
this.decompressorType = decompressorType;
|
this.decompressorType = decompressorType;
|
||||||
this.headHex = headHex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,45 +47,93 @@ public enum CompressType {
|
|||||||
* @return 压缩操作对象
|
* @return 压缩操作对象
|
||||||
* @throws Exception 实例化失败
|
* @throws Exception 实例化失败
|
||||||
*/
|
*/
|
||||||
public Compressor getCompressor() throws Exception {
|
public Compressor<?> getCompressor() throws Exception {
|
||||||
return Ref.newInstance(compressorType);
|
return Ref.newInstance(compressorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取带源路径的压缩操作对象
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 压缩操作对象
|
||||||
|
* @throws Exception 实例化失败
|
||||||
|
*/
|
||||||
|
public Compressor<?> ofCompress(String fromPath) throws Exception {
|
||||||
|
return getCompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取带源文件的压缩操作对象
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 压缩操作对象
|
||||||
|
* @throws Exception 实例化失败
|
||||||
|
*/
|
||||||
|
public Compressor<?> ofCompress(File fromFile) throws Exception {
|
||||||
|
return getCompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取解压操作对象
|
* 获取解压操作对象
|
||||||
*
|
*
|
||||||
* @return 解压操作对象
|
* @return 解压操作对象
|
||||||
* @throws Exception 实例化失败
|
* @throws Exception 实例化失败
|
||||||
*/
|
*/
|
||||||
public Decompressor getDecompressor() throws Exception {
|
public Decompressor<?> getDecompressor() throws Exception {
|
||||||
return Ref.newInstance(decompressorType);
|
return Ref.newInstance(decompressorType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据文件获取解压操作对象。将会读取文件头解析算法类型
|
* 获取带源文件的解压操作对象
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @return 解压操作对象
|
||||||
|
* @throws Exception 实例化失败
|
||||||
|
*/
|
||||||
|
public Decompressor<?> ofCompressed(File fromFile) throws Exception {
|
||||||
|
return getDecompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取带源输入流的解压操作对象
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 解压操作对象
|
||||||
|
* @throws Exception 实例化失败
|
||||||
|
*/
|
||||||
|
public Decompressor<?> ofCompressed(InputStream fromStream) throws Exception {
|
||||||
|
return getDecompressor().from(fromStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件获取解压操作对象
|
||||||
|
* 会读取文件头识别压缩格式
|
||||||
*
|
*
|
||||||
* @param file 文件
|
* @param file 文件
|
||||||
* @return 解压操作对象
|
* @return 解压操作对象
|
||||||
* @throws UnsupportedOperationException 不支持的文件
|
* @throws UnsupportedOperationException 不支持的文件
|
||||||
* @throws Exception 实例化失败
|
* @throws Exception 实例化失败
|
||||||
*/
|
*/
|
||||||
public static Decompressor fromFile(File file) throws Exception {
|
public static Decompressor<?> fromFile(File file) throws Exception {
|
||||||
InputStream is = IO.getInputStream(file);
|
try (InputStream inputStream = IO.getInputStream(file)) {
|
||||||
byte[] head = new byte[4];
|
byte[] head = new byte[512];
|
||||||
if (-1 == is.read(head)) {
|
int length = inputStream.read(head);
|
||||||
|
if (length == -1) {
|
||||||
|
throw new UnsupportedOperationException("empty file");
|
||||||
|
}
|
||||||
|
if (SevenZFile.matches(head, length)) {
|
||||||
|
return Z7.ofCompressed(file);
|
||||||
|
}
|
||||||
|
if (ZipArchiveInputStream.matches(head, length)) {
|
||||||
|
return ZIP.ofCompressed(file);
|
||||||
|
}
|
||||||
|
if (GzipCompressorInputStream.matches(head, length)) {
|
||||||
|
return GZIP.ofCompressed(file);
|
||||||
|
}
|
||||||
|
if (TarArchiveInputStream.matches(head, length)) {
|
||||||
|
return TAR.ofCompressed(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
throw new UnsupportedOperationException("not support file");
|
throw new UnsupportedOperationException("not support file");
|
||||||
}
|
}
|
||||||
is.close();
|
|
||||||
int headHex = 0;
|
|
||||||
for (byte b : head) {
|
|
||||||
headHex <<= 8;
|
|
||||||
headHex |= b;
|
|
||||||
}
|
|
||||||
for (CompressType type : values()) {
|
|
||||||
if (type.headHex == headHex) {
|
|
||||||
return type.getDecompressor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new UnsupportedOperationException("not support headHex");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,120 @@
|
|||||||
package com.imyeyu.compress;
|
package com.imyeyu.compress;
|
||||||
|
|
||||||
|
import com.imyeyu.io.IO;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 抽象压缩器
|
||||||
*
|
*
|
||||||
*
|
* @param <T> 压缩器类型
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 10:34
|
* @version 2024-06-30 10:34
|
||||||
*/
|
*/
|
||||||
public abstract class Compressor extends AbstractCompressor {
|
public abstract class Compressor<T extends Compressor<T>> extends AbstractRunner<T> {
|
||||||
|
|
||||||
public abstract void run(String fromPath, File toFile) throws Exception;
|
/** 源路径 */
|
||||||
|
private String fromPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定源路径
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 当前压缩器
|
||||||
|
*/
|
||||||
|
public T from(String fromPath) {
|
||||||
|
TimiException.required(fromPath, "not found fromPath");
|
||||||
|
this.fromPath = fromPath;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定源文件
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 当前压缩器
|
||||||
|
*/
|
||||||
|
public T from(File fromFile) {
|
||||||
|
TimiException.required(fromFile, "not found fromFile");
|
||||||
|
return from(fromFile.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩到目标文件
|
||||||
|
*
|
||||||
|
* @param toFile 目标压缩文件
|
||||||
|
* @return 当前压缩器
|
||||||
|
* @throws Exception 压缩失败
|
||||||
|
*/
|
||||||
|
public T toFile(File toFile) throws Exception {
|
||||||
|
toFile(requireFromPath(), toFile);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩到目标文件路径
|
||||||
|
*
|
||||||
|
* @param toPath 目标压缩文件路径
|
||||||
|
* @return 当前压缩器
|
||||||
|
* @throws Exception 压缩失败
|
||||||
|
*/
|
||||||
|
public T toFile(String toPath) throws Exception {
|
||||||
|
return toFile(IO.file(toPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩到输出流
|
||||||
|
* 输出流由调用方管理
|
||||||
|
*
|
||||||
|
* @param toStream 目标输出流
|
||||||
|
* @return 当前压缩器
|
||||||
|
* @throws Exception 压缩失败
|
||||||
|
*/
|
||||||
|
public T toStream(OutputStream toStream) throws Exception {
|
||||||
|
toStream(requireFromPath(), toStream);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取已绑定的源路径
|
||||||
|
*
|
||||||
|
* @return 源路径
|
||||||
|
*/
|
||||||
|
protected String requireFromPath() {
|
||||||
|
TimiException.required(fromPath, "not found source");
|
||||||
|
return fromPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行压缩到目标文件
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @param toFile 目标压缩文件
|
||||||
|
* @throws Exception 压缩失败
|
||||||
|
*/
|
||||||
|
protected void toFile(String fromPath, File toFile) throws Exception {
|
||||||
|
try (OutputStream outputStream = IO.getOutputStream(toFile)) {
|
||||||
|
toStream(fromPath, outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
if (isInterrupt) {
|
||||||
|
IO.destroy(toFile);
|
||||||
|
}
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
if (isInterrupt) {
|
||||||
|
IO.destroy(toFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行压缩到输出流
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @param toStream 目标输出流
|
||||||
|
* @throws Exception 压缩失败
|
||||||
|
*/
|
||||||
|
protected abstract void toStream(String fromPath, OutputStream toStream) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,101 @@
|
|||||||
package com.imyeyu.compress;
|
package com.imyeyu.compress;
|
||||||
|
|
||||||
|
import com.imyeyu.io.IO;
|
||||||
|
import com.imyeyu.java.bean.timi.TimiException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽象解压
|
* 抽象解压器
|
||||||
*
|
*
|
||||||
|
* @param <T> 解压器类型
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 18:02
|
* @version 2024-06-30 18:02
|
||||||
*/
|
*/
|
||||||
public abstract class Decompressor extends AbstractCompressor {
|
public abstract class Decompressor<T extends Decompressor<T>> extends AbstractRunner<T> {
|
||||||
|
|
||||||
|
/** 源压缩文件 */
|
||||||
|
private File fromFile;
|
||||||
|
|
||||||
|
/** 源压缩输入流 */
|
||||||
|
private InputStream fromStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行解压
|
* 绑定源压缩文件
|
||||||
*
|
*
|
||||||
* @param fromFile 来源压缩文件
|
* @param fromFile 源压缩文件
|
||||||
* @param toPath 解压路径
|
* @return 当前解压器
|
||||||
|
*/
|
||||||
|
public T from(File fromFile) {
|
||||||
|
TimiException.required(fromFile, "not found fromFile");
|
||||||
|
this.fromFile = fromFile;
|
||||||
|
this.fromStream = null;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定源压缩文件路径
|
||||||
|
*
|
||||||
|
* @param fromPath 源压缩文件路径
|
||||||
|
* @return 当前解压器
|
||||||
|
*/
|
||||||
|
public T from(String fromPath) {
|
||||||
|
TimiException.required(fromPath, "not found fromPath");
|
||||||
|
return from(new File(fromPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定源压缩输入流
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 当前解压器
|
||||||
|
*/
|
||||||
|
public T from(InputStream fromStream) {
|
||||||
|
TimiException.required(fromStream, "not found fromStream");
|
||||||
|
this.fromStream = fromStream;
|
||||||
|
this.fromFile = null;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压到目标目录
|
||||||
|
*
|
||||||
|
* @param toPath 目标目录
|
||||||
|
* @return 当前解压器
|
||||||
* @throws Exception 解压失败
|
* @throws Exception 解压失败
|
||||||
*/
|
*/
|
||||||
public abstract void run(File fromFile, String toPath) throws Exception;
|
public T toPath(String toPath) throws Exception {
|
||||||
|
if (fromFile != null) {
|
||||||
|
toPath(fromFile, toPath);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
if (fromStream != null) {
|
||||||
|
toPath(fromStream, toPath);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("not found source");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行从文件解压
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @param toPath 目标目录
|
||||||
|
* @throws Exception 解压失败
|
||||||
|
*/
|
||||||
|
protected void toPath(File fromFile, String toPath) throws Exception {
|
||||||
|
try (InputStream inputStream = IO.getInputStream(fromFile)) {
|
||||||
|
toPath(inputStream, toPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行从输入流解压
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @param toPath 目标目录
|
||||||
|
* @throws Exception 解压失败
|
||||||
|
*/
|
||||||
|
protected abstract void toPath(InputStream fromStream, String toPath) throws Exception;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,54 +7,64 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
|
|||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gzip 压缩器
|
||||||
|
* 当前实现保持与原行为一致,实际输出为 tar.gz
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:40
|
* @version 2024-06-30 19:40
|
||||||
*/
|
*/
|
||||||
public class GZipCompressor extends Compressor {
|
public class GZipCompressor extends Compressor<GZipCompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static GZipCompressor of(String fromPath) {
|
||||||
|
return new GZipCompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static GZipCompressor of(File fromFile) {
|
||||||
|
return new GZipCompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(String fromPath, File toFile) throws Exception {
|
protected void toStream(String fromPath, OutputStream toStream) throws Exception {
|
||||||
List<File> files = IO.listFile(new File(fromPath));
|
File fromFile = new File(fromPath);
|
||||||
|
List<File> files = IO.listFile(fromFile);
|
||||||
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
||||||
|
initByteProgress(IO.calcSize(fromFile));
|
||||||
OutputStream os = IO.getOutputStream(toFile);
|
try (
|
||||||
BufferedOutputStream byteOS = new BufferedOutputStream(os);
|
GzipCompressorOutputStream gzipOutputStream = new GzipCompressorOutputStream(new BufferedOutputStream(nonClosing(toStream)));
|
||||||
GzipCompressorOutputStream gzipOS = new GzipCompressorOutputStream(byteOS);
|
TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream(gzipOutputStream)
|
||||||
TarArchiveOutputStream tarOS = new TarArchiveOutputStream(gzipOS);
|
) {
|
||||||
|
for (File sourceFile : files) {
|
||||||
TarArchiveEntry tarEntry;
|
String name = sourceFile.getAbsolutePath().substring(basePath.length() + 1);
|
||||||
for (int i = 0, total = files.size(); i < total; i++) {
|
TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, normalizeEntryName(name));
|
||||||
String name = files.get(i).getAbsolutePath().substring(basePath.length() + 1);
|
tarOutputStream.putArchiveEntry(tarEntry);
|
||||||
tarEntry = new TarArchiveEntry(files.get(i), name.replaceAll("\\\\", "/"));
|
try (InputStream inputStream = IO.getInputStream(sourceFile)) {
|
||||||
tarOS.putArchiveEntry(tarEntry);
|
transfer(inputStream, tarOutputStream);
|
||||||
tarOS.write(IO.toBytes(files.get(i)));
|
|
||||||
tarOS.closeArchiveEntry();
|
|
||||||
|
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
}
|
||||||
|
tarOutputStream.closeArchiveEntry();
|
||||||
|
handleFile(sourceFile);
|
||||||
}
|
}
|
||||||
if (fileCallback != null) {
|
tarOutputStream.finish();
|
||||||
fileCallback.handler(toFile);
|
gzipOutputStream.finish();
|
||||||
|
finishProgress();
|
||||||
|
} finally {
|
||||||
|
resetProgress();
|
||||||
}
|
}
|
||||||
if (progressCallback != null) {
|
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarOS.finish();
|
|
||||||
tarOS.close();
|
|
||||||
gzipOS.finish();
|
|
||||||
gzipOS.close();
|
|
||||||
byteOS.flush();
|
|
||||||
byteOS.close();
|
|
||||||
os.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,60 +7,105 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Gzip 解压器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:47
|
* @version 2024-06-30 19:47
|
||||||
*/
|
*/
|
||||||
public class GZipDecompressor extends Decompressor {
|
public class GZipDecompressor extends Decompressor<GZipDecompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static GZipDecompressor of(File fromFile) {
|
||||||
|
return new GZipDecompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromPath 源压缩文件路径
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static GZipDecompressor of(String fromPath) {
|
||||||
|
return new GZipDecompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static GZipDecompressor of(InputStream fromStream) {
|
||||||
|
return new GZipDecompressor().from(fromStream);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(File fromFile, String toPath) throws Exception {
|
protected void toPath(File fromFile, String toPath) throws Exception {
|
||||||
InputStream is = IO.getInputStream(fromFile);
|
initByteProgress(readTotalSize(fromFile));
|
||||||
GzipCompressorInputStream gzipIS = new GzipCompressorInputStream(is);
|
try {
|
||||||
TarArchiveInputStream tarIS = new TarArchiveInputStream(gzipIS);
|
super.toPath(fromFile, toPath);
|
||||||
|
} finally {
|
||||||
|
resetProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int total = 0;
|
@Override
|
||||||
{
|
protected void toPath(InputStream fromStream, String toPath) throws Exception {
|
||||||
|
try (
|
||||||
|
GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(nonClosing(fromStream));
|
||||||
|
TarArchiveInputStream tarInputStream = new TarArchiveInputStream(gzipInputStream)
|
||||||
|
) {
|
||||||
TarArchiveEntry entry;
|
TarArchiveEntry entry;
|
||||||
while ((entry = tarIS.getNextEntry()) != null) {
|
boolean processed = false;
|
||||||
if (!entry.isDirectory()) {
|
while ((entry = tarInputStream.getNextEntry()) != null) {
|
||||||
total++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarIS.close();
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
is = IO.getInputStream(fromFile);
|
|
||||||
tarIS = new TarArchiveInputStream(is);
|
|
||||||
TarArchiveEntry entry;
|
|
||||||
for (int i = 0; (entry = tarIS.getNextEntry()) != null; i++) {
|
|
||||||
String path = IO.fitPath(toPath) + entry.getName();
|
String path = IO.fitPath(toPath) + entry.getName();
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
IO.dir(path);
|
IO.dir(path);
|
||||||
} else {
|
} else {
|
||||||
File toFile = IO.file(path);
|
File toFile = IO.file(path);
|
||||||
IO.toFile(toFile, tarIS);
|
try (OutputStream outputStream = IO.getOutputStream(toFile)) {
|
||||||
|
transfer(tarInputStream, outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
handleFile(toFile);
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
finishProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fileCallback != null) {
|
/**
|
||||||
fileCallback.handler(toFile);
|
* 读取压缩包解压总字节数
|
||||||
|
*
|
||||||
|
* @param fromFile 压缩文件
|
||||||
|
* @return 总字节数
|
||||||
|
* @throws Exception 读取失败
|
||||||
|
*/
|
||||||
|
private long readTotalSize(File fromFile) throws Exception {
|
||||||
|
long totalSize = 0L;
|
||||||
|
try (
|
||||||
|
InputStream inputStream = IO.getInputStream(fromFile);
|
||||||
|
GzipCompressorInputStream gzipInputStream = new GzipCompressorInputStream(inputStream);
|
||||||
|
TarArchiveInputStream tarInputStream = new TarArchiveInputStream(gzipInputStream)
|
||||||
|
) {
|
||||||
|
TarArchiveEntry entry;
|
||||||
|
while ((entry = tarInputStream.getNextEntry()) != null) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
totalSize += entry.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
}
|
||||||
}
|
return totalSize;
|
||||||
if (progressCallback != null && total != -1) {
|
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarIS.close();
|
|
||||||
gzipIS.close();
|
|
||||||
is.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,56 +6,59 @@ import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
|||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tar 压缩器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:48
|
* @version 2024-06-30 19:48
|
||||||
*/
|
*/
|
||||||
public class TarCompressor extends Compressor {
|
public class TarCompressor extends Compressor<TarCompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static TarCompressor of(String fromPath) {
|
||||||
|
return new TarCompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static TarCompressor of(File fromFile) {
|
||||||
|
return new TarCompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(String fromPath, File toFile) throws Exception {
|
protected void toStream(String fromPath, OutputStream toStream) throws Exception {
|
||||||
List<File> files = IO.listFile(new File(fromPath));
|
File fromFile = new File(fromPath);
|
||||||
|
List<File> files = IO.listFile(fromFile);
|
||||||
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
||||||
|
initByteProgress(IO.calcSize(fromFile));
|
||||||
OutputStream os = IO.getOutputStream(toFile);
|
try (TarArchiveOutputStream tarOutputStream = new TarArchiveOutputStream(new BufferedOutputStream(nonClosing(toStream)))) {
|
||||||
BufferedOutputStream byteOS = new BufferedOutputStream(os);
|
for (File sourceFile : files) {
|
||||||
TarArchiveOutputStream tarOS = new TarArchiveOutputStream(byteOS);
|
String name = sourceFile.getAbsolutePath().substring(basePath.length() + 1);
|
||||||
|
TarArchiveEntry tarEntry = new TarArchiveEntry(sourceFile, normalizeEntryName(name));
|
||||||
TarArchiveEntry tarEntry;
|
tarOutputStream.putArchiveEntry(tarEntry);
|
||||||
for (int i = 0, total = files.size(); i < total; i++) {
|
try (InputStream inputStream = IO.getInputStream(sourceFile)) {
|
||||||
String name = files.get(i).getAbsolutePath().substring(basePath.length() + 1);
|
transfer(inputStream, tarOutputStream);
|
||||||
tarEntry = new TarArchiveEntry(files.get(i), name.replaceAll("\\\\", "/"));
|
|
||||||
tarOS.putArchiveEntry(tarEntry);
|
|
||||||
tarOS.write(IO.toBytes(files.get(i)));
|
|
||||||
tarOS.closeArchiveEntry();
|
|
||||||
|
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
}
|
||||||
|
tarOutputStream.closeArchiveEntry();
|
||||||
|
handleFile(sourceFile);
|
||||||
}
|
}
|
||||||
if (fileCallback != null) {
|
tarOutputStream.finish();
|
||||||
fileCallback.handler(toFile);
|
finishProgress();
|
||||||
}
|
} finally {
|
||||||
if (progressCallback != null) {
|
resetProgress();
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarOS.finish();
|
|
||||||
tarOS.close();
|
|
||||||
byteOS.flush();
|
|
||||||
byteOS.close();
|
|
||||||
os.flush();
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
if (isInterrupt) {
|
|
||||||
IO.destroy(toFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,58 +6,98 @@ import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tar 解压器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:48
|
* @version 2024-06-30 19:48
|
||||||
*/
|
*/
|
||||||
public class TarDecompressor extends Decompressor {
|
public class TarDecompressor extends Decompressor<TarDecompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static TarDecompressor of(File fromFile) {
|
||||||
|
return new TarDecompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromPath 源压缩文件路径
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static TarDecompressor of(String fromPath) {
|
||||||
|
return new TarDecompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static TarDecompressor of(InputStream fromStream) {
|
||||||
|
return new TarDecompressor().from(fromStream);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(File fromFile, String toPath) throws Exception {
|
protected void toPath(File fromFile, String toPath) throws Exception {
|
||||||
InputStream is = IO.getInputStream(fromFile);
|
initByteProgress(readTotalSize(fromFile));
|
||||||
TarArchiveInputStream tarIS = new TarArchiveInputStream(is);
|
try {
|
||||||
|
super.toPath(fromFile, toPath);
|
||||||
|
} finally {
|
||||||
|
resetProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int total = 0;
|
@Override
|
||||||
{
|
protected void toPath(InputStream fromStream, String toPath) throws Exception {
|
||||||
|
try (TarArchiveInputStream tarInputStream = new TarArchiveInputStream(nonClosing(fromStream))) {
|
||||||
TarArchiveEntry entry;
|
TarArchiveEntry entry;
|
||||||
while ((entry = tarIS.getNextEntry()) != null) {
|
boolean processed = false;
|
||||||
if (!entry.isDirectory()) {
|
while ((entry = tarInputStream.getNextEntry()) != null) {
|
||||||
total++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarIS.close();
|
|
||||||
is.close();
|
|
||||||
|
|
||||||
is = IO.getInputStream(fromFile);
|
|
||||||
tarIS = new TarArchiveInputStream(is);
|
|
||||||
TarArchiveEntry entry;
|
|
||||||
for (int i = 0; (entry = tarIS.getNextEntry()) != null; i++) {
|
|
||||||
String path = IO.fitPath(toPath) + entry.getName();
|
String path = IO.fitPath(toPath) + entry.getName();
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
IO.dir(path);
|
IO.dir(path);
|
||||||
} else {
|
} else {
|
||||||
File toFile = IO.file(path);
|
File toFile = IO.file(path);
|
||||||
IO.toFile(toFile, tarIS);
|
try (OutputStream outputStream = IO.getOutputStream(toFile)) {
|
||||||
|
transfer(tarInputStream, outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
handleFile(toFile);
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
finishProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fileCallback != null) {
|
/**
|
||||||
fileCallback.handler(toFile);
|
* 读取压缩包解压总字节数
|
||||||
|
*
|
||||||
|
* @param fromFile 压缩文件
|
||||||
|
* @return 总字节数
|
||||||
|
* @throws Exception 读取失败
|
||||||
|
*/
|
||||||
|
private long readTotalSize(File fromFile) throws Exception {
|
||||||
|
long totalSize = 0L;
|
||||||
|
try (InputStream inputStream = IO.getInputStream(fromFile); TarArchiveInputStream tarInputStream = new TarArchiveInputStream(inputStream)) {
|
||||||
|
TarArchiveEntry entry;
|
||||||
|
while ((entry = tarInputStream.getNextEntry()) != null) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
totalSize += entry.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
}
|
||||||
}
|
return totalSize;
|
||||||
if (progressCallback != null && total != -1) {
|
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tarIS.close();
|
|
||||||
is.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,46 +5,101 @@ import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
|
|||||||
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
|
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 7z 压缩器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:40
|
* @version 2024-06-30 19:40
|
||||||
*/
|
*/
|
||||||
public class Z7Compressor extends Compressor {
|
public class Z7Compressor extends Compressor<Z7Compressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static Z7Compressor of(String fromPath) {
|
||||||
|
return new Z7Compressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static Z7Compressor of(File fromFile) {
|
||||||
|
return new Z7Compressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(String fromPath, File toFile) throws Exception {
|
protected void toStream(String fromPath, OutputStream toStream) throws Exception {
|
||||||
List<File> files = IO.listFile(new File(fromPath));
|
Path tempFile = Files.createTempFile("timi-compress-", ".7z");
|
||||||
|
try {
|
||||||
|
from(fromPath).toFile(tempFile.toFile());
|
||||||
|
try (InputStream inputStream = Files.newInputStream(tempFile)) {
|
||||||
|
transfer(inputStream, toStream, false);
|
||||||
|
toStream.flush();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void toFile(String fromPath, File toFile) throws Exception {
|
||||||
|
File fromFile = new File(fromPath);
|
||||||
|
List<File> files = IO.listFile(fromFile);
|
||||||
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
||||||
|
initByteProgress(IO.calcSize(fromFile));
|
||||||
SevenZOutputFile out = new SevenZOutputFile(toFile);
|
try (SevenZOutputFile outputFile = new SevenZOutputFile(toFile)) {
|
||||||
SevenZArchiveEntry entry;
|
for (File sourceFile : files) {
|
||||||
for (int i = 0, total = files.size(); i < total; i++) {
|
String name = sourceFile.getAbsolutePath().substring(basePath.length() + 1);
|
||||||
String name = files.get(i).getAbsolutePath().substring(basePath.length() + 1);
|
SevenZArchiveEntry entry = outputFile.createArchiveEntry(sourceFile, normalizeEntryName(name));
|
||||||
entry = out.createArchiveEntry(files.get(i), name.replaceAll("\\\\", "/"));
|
outputFile.putArchiveEntry(entry);
|
||||||
out.putArchiveEntry(entry);
|
writeSevenZEntry(outputFile, sourceFile);
|
||||||
out.write(IO.toBytes(files.get(i)));
|
outputFile.closeArchiveEntry();
|
||||||
out.closeArchiveEntry();
|
handleFile(sourceFile);
|
||||||
|
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fileCallback != null) {
|
|
||||||
fileCallback.handler(toFile);
|
|
||||||
}
|
|
||||||
if (progressCallback != null) {
|
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
}
|
||||||
|
outputFile.finish();
|
||||||
|
finishProgress();
|
||||||
|
} catch (Exception exception) {
|
||||||
if (isInterrupt) {
|
if (isInterrupt) {
|
||||||
break;
|
IO.destroy(toFile);
|
||||||
}
|
}
|
||||||
|
throw exception;
|
||||||
|
} finally {
|
||||||
|
resetProgress();
|
||||||
}
|
}
|
||||||
out.close();
|
|
||||||
if (isInterrupt) {
|
if (isInterrupt) {
|
||||||
IO.destroy(toFile);
|
IO.destroy(toFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 写入 7z 条目内容
|
||||||
|
*
|
||||||
|
* @param outputFile 7z 输出文件
|
||||||
|
* @param sourceFile 源文件
|
||||||
|
* @throws Exception 写入失败
|
||||||
|
*/
|
||||||
|
private void writeSevenZEntry(SevenZOutputFile outputFile, File sourceFile) throws Exception {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
try (InputStream inputStream = IO.getInputStream(sourceFile)) {
|
||||||
|
int length;
|
||||||
|
while ((length = inputStream.read(buffer)) != -1) {
|
||||||
|
awaitIfPaused();
|
||||||
|
ensureRunning();
|
||||||
|
outputFile.write(buffer, 0, length);
|
||||||
|
handleTransferred(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,53 +5,143 @@ import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
|
|||||||
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
|
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 7z 解压器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:42
|
* @version 2024-06-30 19:42
|
||||||
*/
|
*/
|
||||||
public class Z7Decompressor extends Decompressor {
|
public class Z7Decompressor extends Decompressor<Z7Decompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static Z7Decompressor of(File fromFile) {
|
||||||
|
return new Z7Decompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromPath 源压缩文件路径
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static Z7Decompressor of(String fromPath) {
|
||||||
|
return new Z7Decompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static Z7Decompressor of(InputStream fromStream) {
|
||||||
|
return new Z7Decompressor().from(fromStream);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(File fromFile, String toPath) throws Exception {
|
protected void toPath(File fromFile, String toPath) throws Exception {
|
||||||
toPath = new File(toPath).getAbsolutePath() + SEP;
|
initByteProgress(readTotalSize(fromFile));
|
||||||
|
try {
|
||||||
|
super.toPath(fromFile, toPath);
|
||||||
|
} finally {
|
||||||
|
resetProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int total = 0;
|
@Override
|
||||||
if (progressCallback != null) {
|
protected void toPath(InputStream fromStream, String toPath) throws Exception {
|
||||||
SevenZFile file = SevenZFile.builder().setFile(fromFile).get();
|
Path tempFile = writeTempFile(fromStream);
|
||||||
try (file) {
|
try (SevenZFile file = SevenZFile.builder().setFile(tempFile.toFile()).get()) {
|
||||||
while (file.getNextEntry() != null) {
|
|
||||||
total++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SevenZFile file = SevenZFile.builder().setFile(fromFile).get();
|
|
||||||
SevenZArchiveEntry entry;
|
SevenZArchiveEntry entry;
|
||||||
for (int i = 0; (entry = file.getNextEntry()) != null; i++) {
|
boolean processed = false;
|
||||||
|
while ((entry = file.getNextEntry()) != null) {
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
IO.dir(IO.fitPath(toPath) + entry.getName());
|
IO.dir(IO.fitPath(toPath) + entry.getName());
|
||||||
} else {
|
} else {
|
||||||
File toFile = IO.file(toPath + entry.getName());
|
File toFile = IO.file(IO.fitPath(toPath) + entry.getName());
|
||||||
byte[] buffer = new byte[(int) entry.getSize()];
|
try (OutputStream outputStream = IO.getOutputStream(toFile)) {
|
||||||
file.read(buffer, 0, buffer.length);
|
copySevenZEntry(file, entry, outputStream);
|
||||||
IO.toFile(toFile, buffer);
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
handleFile(toFile);
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
finishProgress();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Files.deleteIfExists(tempFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fileCallback != null) {
|
/**
|
||||||
fileCallback.handler(toFile);
|
* 将输入流写入临时文件
|
||||||
|
*
|
||||||
|
* @param fromStream 输入流
|
||||||
|
* @return 临时文件路径
|
||||||
|
* @throws Exception 写入失败
|
||||||
|
*/
|
||||||
|
private Path writeTempFile(InputStream fromStream) throws Exception {
|
||||||
|
Path tempFile = Files.createTempFile("timi-compress-", ".7z");
|
||||||
|
try (OutputStream outputStream = Files.newOutputStream(tempFile)) {
|
||||||
|
transfer(fromStream, outputStream, false);
|
||||||
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
return tempFile;
|
||||||
}
|
}
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
/**
|
||||||
pauseLock.wait();
|
* 复制当前 7z 条目数据
|
||||||
}
|
*
|
||||||
}
|
* @param file 7z 文件
|
||||||
if (progressCallback != null && total != -1) {
|
* @param entry 当前条目
|
||||||
progressCallback.handler(1D * i / total);
|
* @param toStream 输出流
|
||||||
}
|
* @throws Exception 复制失败
|
||||||
if (isInterrupt) {
|
*/
|
||||||
|
private void copySevenZEntry(SevenZFile file, SevenZArchiveEntry entry, OutputStream toStream) throws Exception {
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
long remain = entry.getSize();
|
||||||
|
while (0 < remain) {
|
||||||
|
awaitIfPaused();
|
||||||
|
ensureRunning();
|
||||||
|
int length = file.read(buffer, 0, (int) Math.min(buffer.length, remain));
|
||||||
|
if (length < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
toStream.write(buffer, 0, length);
|
||||||
|
remain -= length;
|
||||||
|
handleTransferred(length);
|
||||||
}
|
}
|
||||||
file.close();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取压缩包解压总字节数
|
||||||
|
*
|
||||||
|
* @param fromFile 压缩文件
|
||||||
|
* @return 总字节数
|
||||||
|
* @throws Exception 读取失败
|
||||||
|
*/
|
||||||
|
private long readTotalSize(File fromFile) throws Exception {
|
||||||
|
long totalSize = 0L;
|
||||||
|
try (SevenZFile file = SevenZFile.builder().setFile(fromFile).get()) {
|
||||||
|
SevenZArchiveEntry entry;
|
||||||
|
while ((entry = file.getNextEntry()) != null) {
|
||||||
|
if (!entry.isDirectory()) {
|
||||||
|
totalSize += entry.getSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,57 +4,60 @@ import com.imyeyu.io.IO;
|
|||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Zip 压缩器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:46
|
* @version 2024-06-30 19:46
|
||||||
*/
|
*/
|
||||||
public class ZipCompressor extends Compressor {
|
public class ZipCompressor extends Compressor<ZipCompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromPath 源路径
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static ZipCompressor of(String fromPath) {
|
||||||
|
return new ZipCompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建压缩器
|
||||||
|
*
|
||||||
|
* @param fromFile 源文件
|
||||||
|
* @return 压缩器
|
||||||
|
*/
|
||||||
|
public static ZipCompressor of(File fromFile) {
|
||||||
|
return new ZipCompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(String fromPath, File toFile) throws Exception {
|
protected void toStream(String fromPath, OutputStream toStream) throws Exception {
|
||||||
List<File> files = IO.listFile(new File(fromPath));
|
File fromFile = new File(fromPath);
|
||||||
|
List<File> files = IO.listFile(fromFile);
|
||||||
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
String basePath = files.getFirst().getParentFile().getAbsolutePath();
|
||||||
|
initByteProgress(IO.calcSize(fromFile));
|
||||||
OutputStream os = IO.getOutputStream(toFile);
|
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(nonClosing(toStream)))) {
|
||||||
BufferedOutputStream byteOS = new BufferedOutputStream(os);
|
for (File sourceFile : files) {
|
||||||
ZipOutputStream zipOS = new ZipOutputStream(byteOS);
|
String name = sourceFile.getAbsolutePath().substring(basePath.length() + 1);
|
||||||
|
zipOutputStream.putNextEntry(new ZipEntry(normalizeEntryName(name)));
|
||||||
for (int i = 0, total = files.size(); i < total; i++) {
|
try (InputStream inputStream = IO.getInputStream(sourceFile)) {
|
||||||
String name = files.get(i).getAbsolutePath().substring(basePath.length() + 1);
|
transfer(inputStream, zipOutputStream);
|
||||||
ZipEntry zipEntry = new ZipEntry(name.replaceAll("\\\\", "/"));
|
|
||||||
zipOS.putNextEntry(zipEntry);
|
|
||||||
zipOS.write(IO.toBytes(files.get(i)));
|
|
||||||
zipOS.closeEntry();
|
|
||||||
|
|
||||||
if (isPause) {
|
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
}
|
||||||
|
zipOutputStream.closeEntry();
|
||||||
|
handleFile(sourceFile);
|
||||||
}
|
}
|
||||||
if (fileCallback != null) {
|
zipOutputStream.finish();
|
||||||
fileCallback.handler(toFile);
|
finishProgress();
|
||||||
}
|
} finally {
|
||||||
if (progressCallback != null) {
|
resetProgress();
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zipOS.finish();
|
|
||||||
zipOS.close();
|
|
||||||
byteOS.flush();
|
|
||||||
byteOS.close();
|
|
||||||
os.flush();
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
if (isInterrupt) {
|
|
||||||
IO.destroy(toFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,54 +3,110 @@ package com.imyeyu.compress;
|
|||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Zip 解压器
|
||||||
|
*
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
* @version 2024-06-30 19:47
|
* @version 2024-06-30 19:47
|
||||||
*/
|
*/
|
||||||
public class ZipDecompressor extends Decompressor {
|
public class ZipDecompressor extends Decompressor<ZipDecompressor> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromFile 源压缩文件
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static ZipDecompressor of(File fromFile) {
|
||||||
|
return new ZipDecompressor().from(fromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromPath 源压缩文件路径
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static ZipDecompressor of(String fromPath) {
|
||||||
|
return new ZipDecompressor().from(fromPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建解压器
|
||||||
|
*
|
||||||
|
* @param fromStream 源压缩输入流
|
||||||
|
* @return 解压器
|
||||||
|
*/
|
||||||
|
public static ZipDecompressor of(InputStream fromStream) {
|
||||||
|
return new ZipDecompressor().from(fromStream);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(File fromFile, String toPath) throws Exception {
|
protected void toPath(File fromFile, String toPath) throws Exception {
|
||||||
ZipFile zip = new ZipFile(fromFile);
|
initByteProgress(readTotalSize(fromFile));
|
||||||
|
try {
|
||||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
super.toPath(fromFile, toPath);
|
||||||
int total = 0;
|
} finally {
|
||||||
while (entries.hasMoreElements()) {
|
resetProgress();
|
||||||
if (!entries.nextElement().isDirectory()) {
|
|
||||||
total++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entries = zip.entries();
|
@Override
|
||||||
|
protected void toPath(InputStream fromStream, String toPath) throws Exception {
|
||||||
|
try (ZipInputStream zipInputStream = new ZipInputStream(nonClosing(fromStream))) {
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
for (int i = 0; entries.hasMoreElements(); i++) {
|
boolean processed = false;
|
||||||
entry = entries.nextElement();
|
while ((entry = zipInputStream.getNextEntry()) != null) {
|
||||||
|
String path = IO.fitPath(toPath) + entry.getName();
|
||||||
if (entry.isDirectory()) {
|
if (entry.isDirectory()) {
|
||||||
IO.dir(IO.fitPath(toPath) + entry.getName());
|
IO.dir(path);
|
||||||
} else {
|
} else {
|
||||||
File toFile = IO.file(IO.fitPath(toPath) + entry.getName());
|
File toFile = IO.file(path);
|
||||||
IO.toFile(toFile, zip.getInputStream(entry));
|
try (OutputStream outputStream = IO.getOutputStream(toFile)) {
|
||||||
|
transfer(zipInputStream, outputStream);
|
||||||
|
outputStream.flush();
|
||||||
|
}
|
||||||
|
handleFile(toFile);
|
||||||
|
processed = true;
|
||||||
|
}
|
||||||
|
zipInputStream.closeEntry();
|
||||||
|
}
|
||||||
|
if (processed) {
|
||||||
|
finishProgress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fileCallback != null) {
|
/**
|
||||||
fileCallback.handler(toFile);
|
* 读取压缩包解压总字节数
|
||||||
|
*
|
||||||
|
* @param fromFile 压缩文件
|
||||||
|
* @return 总字节数,未知时返回 -1
|
||||||
|
* @throws Exception 读取失败
|
||||||
|
*/
|
||||||
|
private long readTotalSize(File fromFile) throws Exception {
|
||||||
|
long totalSize = 0L;
|
||||||
|
try (ZipFile zipFile = new ZipFile(fromFile)) {
|
||||||
|
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
ZipEntry entry = entries.nextElement();
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
long size = entry.getSize();
|
||||||
|
if (size < 0L) {
|
||||||
|
return -1L;
|
||||||
|
}
|
||||||
|
totalSize += size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPause) {
|
return totalSize;
|
||||||
synchronized (pauseLock) {
|
|
||||||
pauseLock.wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (progressCallback != null) {
|
|
||||||
progressCallback.handler(1D * i / total);
|
|
||||||
}
|
|
||||||
if (isInterrupt) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zip.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import com.imyeyu.compress.CompressType;
|
import com.imyeyu.compress.CompressType;
|
||||||
|
import com.imyeyu.compress.GZipCompressor;
|
||||||
|
import com.imyeyu.compress.GZipDecompressor;
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -13,15 +20,62 @@ import java.io.File;
|
|||||||
public class GzipTest {
|
public class GzipTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompress() throws Exception {
|
public void testStaticCompress() throws Exception {
|
||||||
File out = IO.file("testOut/test.gz");
|
GZipCompressor compressor = GZipCompressor.of("testSrc").toFile("testOut/test.gz");
|
||||||
CompressType.Z7.getCompressor().run("testSrc", out);
|
Assertions.assertNotNull(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainCompress() throws Exception {
|
||||||
|
File out = IO.file("testOut/test-chain.gz");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
GZipCompressor compressor = GZipCompressor.of("testSrc");
|
||||||
|
Assertions.assertSame(
|
||||||
|
compressor,
|
||||||
|
compressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toFile(out)
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecompress() throws Exception {
|
public void testDecompress() throws Exception {
|
||||||
File in = IO.file("testOut/test.gz");
|
File in = IO.file("testOut/test.gz");
|
||||||
|
GZipCompressor.of("testSrc").toFile(in);
|
||||||
File out = IO.dir("testOutDe");
|
File out = IO.dir("testOutDe");
|
||||||
CompressType.fromFile(in).run(in, out.getAbsolutePath());
|
CompressType.fromFile(in).toPath(out.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompressToStream() throws Exception {
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.GZIP.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainDecompressFromStream() throws Exception {
|
||||||
|
byte[] bytes;
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.GZIP.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
bytes = outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
File out = IO.dir("testOutDeStream/gzip");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
GZipDecompressor decompressor = GZipDecompressor.of(new ByteArrayInputStream(bytes));
|
||||||
|
Assertions.assertSame(
|
||||||
|
decompressor,
|
||||||
|
decompressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toPath(out.getAbsolutePath())
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import com.imyeyu.compress.CompressType;
|
import com.imyeyu.compress.CompressType;
|
||||||
|
import com.imyeyu.compress.TarCompressor;
|
||||||
|
import com.imyeyu.compress.TarDecompressor;
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -13,15 +20,62 @@ import java.io.File;
|
|||||||
public class TarTest {
|
public class TarTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompress() throws Exception {
|
public void testStaticCompress() throws Exception {
|
||||||
File out = IO.file("testOut/test.tar");
|
TarCompressor compressor = TarCompressor.of("testSrc").toFile("testOut/test.tar");
|
||||||
CompressType.Z7.getCompressor().run("testSrc", out);
|
Assertions.assertNotNull(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainCompress() throws Exception {
|
||||||
|
File out = IO.file("testOut/test-chain.tar");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
TarCompressor compressor = TarCompressor.of("testSrc");
|
||||||
|
Assertions.assertSame(
|
||||||
|
compressor,
|
||||||
|
compressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toFile(out)
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecompress() throws Exception {
|
public void testDecompress() throws Exception {
|
||||||
File in = IO.file("testOut/test.tar");
|
File in = IO.file("testOut/test.tar");
|
||||||
|
TarCompressor.of("testSrc").toFile(in);
|
||||||
File out = IO.dir("testOutDe");
|
File out = IO.dir("testOutDe");
|
||||||
CompressType.fromFile(in).run(in, out.getAbsolutePath());
|
CompressType.fromFile(in).toPath(out.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompressToStream() throws Exception {
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.TAR.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainDecompressFromStream() throws Exception {
|
||||||
|
byte[] bytes;
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.TAR.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
bytes = outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
File out = IO.dir("testOutDeStream/tar");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
TarDecompressor decompressor = TarDecompressor.of(new ByteArrayInputStream(bytes));
|
||||||
|
Assertions.assertSame(
|
||||||
|
decompressor,
|
||||||
|
decompressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toPath(out.getAbsolutePath())
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import com.imyeyu.compress.CompressType;
|
import com.imyeyu.compress.CompressType;
|
||||||
|
import com.imyeyu.compress.Z7Compressor;
|
||||||
|
import com.imyeyu.compress.Z7Decompressor;
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -13,15 +20,62 @@ import java.io.File;
|
|||||||
public class Z7Test {
|
public class Z7Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompress() throws Exception {
|
public void testStaticCompress() throws Exception {
|
||||||
File out = IO.file("testOut/test.7z");
|
Z7Compressor compressor = Z7Compressor.of("testSrc").toFile("testOut/test.7z");
|
||||||
CompressType.Z7.getCompressor().run("testSrc", out);
|
Assertions.assertNotNull(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainCompress() throws Exception {
|
||||||
|
File out = IO.file("testOut/test-chain.7z");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
Z7Compressor compressor = Z7Compressor.of("testSrc");
|
||||||
|
Assertions.assertSame(
|
||||||
|
compressor,
|
||||||
|
compressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toFile(out)
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecompress() throws Exception {
|
public void testDecompress() throws Exception {
|
||||||
File in = IO.file("testOut/test.7z");
|
File in = IO.file("testOut/test.7z");
|
||||||
|
Z7Compressor.of("testSrc").toFile(in);
|
||||||
File out = IO.dir("testOutDe");
|
File out = IO.dir("testOutDe");
|
||||||
CompressType.fromFile(in).run(in, out.getAbsolutePath());
|
CompressType.fromFile(in).toPath(out.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompressToStream() throws Exception {
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.Z7.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainDecompressFromStream() throws Exception {
|
||||||
|
byte[] bytes;
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.Z7.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
bytes = outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
File out = IO.dir("testOutDeStream/z7");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
Z7Decompressor decompressor = Z7Decompressor.of(new ByteArrayInputStream(bytes));
|
||||||
|
Assertions.assertSame(
|
||||||
|
decompressor,
|
||||||
|
decompressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toPath(out.getAbsolutePath())
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,17 @@
|
|||||||
package test;
|
package test;
|
||||||
|
|
||||||
import com.imyeyu.compress.CompressType;
|
import com.imyeyu.compress.CompressType;
|
||||||
|
import com.imyeyu.compress.ZipCompressor;
|
||||||
|
import com.imyeyu.compress.ZipDecompressor;
|
||||||
import com.imyeyu.io.IO;
|
import com.imyeyu.io.IO;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author 夜雨
|
* @author 夜雨
|
||||||
@@ -13,15 +20,62 @@ import java.io.File;
|
|||||||
public class ZipTest {
|
public class ZipTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCompress() throws Exception {
|
public void testStaticCompress() throws Exception {
|
||||||
File out = IO.file("testOut/test.zip");
|
ZipCompressor compressor = ZipCompressor.of("testSrc").toFile("testOut/test.zip");
|
||||||
CompressType.Z7.getCompressor().run("testSrc", out);
|
Assertions.assertNotNull(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainCompress() throws Exception {
|
||||||
|
File out = IO.file("testOut/test-chain.zip");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
ZipCompressor compressor = ZipCompressor.of("testSrc");
|
||||||
|
Assertions.assertSame(
|
||||||
|
compressor,
|
||||||
|
compressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toFile(out)
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecompress() throws Exception {
|
public void testDecompress() throws Exception {
|
||||||
File in = IO.file("testOut/test.zip");
|
File in = IO.file("testOut/test.zip");
|
||||||
|
ZipCompressor.of("testSrc").toFile(in);
|
||||||
File out = IO.dir("testOutDe");
|
File out = IO.dir("testOutDe");
|
||||||
CompressType.fromFile(in).run(in, out.getAbsolutePath());
|
CompressType.fromFile(in).toPath(out.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompressToStream() throws Exception {
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.ZIP.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChainDecompressFromStream() throws Exception {
|
||||||
|
byte[] bytes;
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
CompressType.ZIP.ofCompress("testSrc").toStream(outputStream);
|
||||||
|
bytes = outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
File out = IO.dir("testOutDeStream/zip");
|
||||||
|
AtomicInteger fileCount = new AtomicInteger();
|
||||||
|
AtomicReference<Double> progress = new AtomicReference<>(0D);
|
||||||
|
ZipDecompressor decompressor = ZipDecompressor.of(new ByteArrayInputStream(bytes));
|
||||||
|
Assertions.assertSame(
|
||||||
|
decompressor,
|
||||||
|
decompressor
|
||||||
|
.fileCallback(file -> fileCount.incrementAndGet())
|
||||||
|
.progressCallback(progress::set)
|
||||||
|
.toPath(out.getAbsolutePath())
|
||||||
|
);
|
||||||
|
Assertions.assertTrue(0 < fileCount.get());
|
||||||
|
Assertions.assertEquals(1D, progress.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user