@@ -0,0 +1,238 @@
|
||||
package com.imyeyu.compress;
|
||||
|
||||
import com.imyeyu.java.bean.CallbackArg;
|
||||
import com.imyeyu.utils.OS;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 抽象压缩执行器
|
||||
*
|
||||
* @author 夜雨
|
||||
* @since 2024-06-30 18:09
|
||||
*/
|
||||
public abstract class AbstractRunner implements OS.FileSystem {
|
||||
|
||||
/** 文件处理回调 */
|
||||
@Setter
|
||||
protected CallbackArg<File> fileCallback;
|
||||
|
||||
/** 进度回调 */
|
||||
@Setter
|
||||
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;
|
||||
|
||||
/** 暂停任务 */
|
||||
public void pause() {
|
||||
isPause = true;
|
||||
}
|
||||
|
||||
/** 恢复任务 */
|
||||
public void start() {
|
||||
isPause = false;
|
||||
synchronized (pauseLock) {
|
||||
pauseLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/** 中断任务 */
|
||||
public void interrupt() {
|
||||
isInterrupt = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化字节进度
|
||||
*
|
||||
* @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) {
|
||||
if (entryName == null || entryName.isBlank()) {
|
||||
throw new IllegalArgumentException("条目名称不能为空");
|
||||
}
|
||||
String normalized = entryName.replace('\\', '/');
|
||||
while (normalized.startsWith("/")) {
|
||||
normalized = normalized.substring(1);
|
||||
}
|
||||
if (normalized.isBlank()) {
|
||||
throw new IllegalArgumentException("条目名称不能为空");
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user