added skip all on error
This commit is contained in:
parent
61998ecc40
commit
f7d63027d4
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ buildNumber.properties
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.claude
|
||||
@ -59,6 +59,7 @@ public class FileOperations {
|
||||
long totalItems = calculateTotalItems(cleanedItems);
|
||||
final long[] currentItem = {0};
|
||||
final OverwriteResponse[] globalResponse = {null};
|
||||
final ErrorResponse[] globalErrorResponse = {null};
|
||||
|
||||
for (FileItem item : cleanedItems) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
@ -95,9 +96,10 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
|
||||
try {
|
||||
if (source.isDirectory()) {
|
||||
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse);
|
||||
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
|
||||
} else {
|
||||
copyFileWithProgress(source, target, totalItems, currentItem, callback);
|
||||
}
|
||||
@ -107,6 +109,7 @@ public class FileOperations {
|
||||
ErrorResponse res = callback.onError(source, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
@ -163,7 +166,7 @@ public class FileOperations {
|
||||
copyPermissions(source, target);
|
||||
}
|
||||
|
||||
private static void copyDirectory(File source, File target, long totalItems, final long[] currentItem, ProgressCallback callback, final OverwriteResponse[] globalResponse) throws IOException {
|
||||
private static void copyDirectory(File source, File target, long totalItems, final long[] currentItem, ProgressCallback callback, final OverwriteResponse[] globalResponse, final ErrorResponse[] globalErrorResponse) throws IOException {
|
||||
if (!target.exists()) {
|
||||
if (!target.mkdirs()) {
|
||||
throw new IOException("Failed to create directory: " + target.getAbsolutePath());
|
||||
@ -179,32 +182,52 @@ public class FileOperations {
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
|
||||
File targetFile = new File(target, file.getName());
|
||||
|
||||
if (file.isDirectory()) {
|
||||
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse);
|
||||
} else {
|
||||
if (targetFile.exists()) {
|
||||
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) {
|
||||
currentItem[0] += countItems(file);
|
||||
continue;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
copyDirectory(file, targetFile, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
|
||||
} else {
|
||||
if (targetFile.exists()) {
|
||||
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) {
|
||||
currentItem[0] += countItems(file);
|
||||
break;
|
||||
}
|
||||
if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) {
|
||||
OverwriteResponse res = callback.confirmOverwrite(file, targetFile);
|
||||
if (res == OverwriteResponse.CANCEL) return;
|
||||
if (res == OverwriteResponse.NO_TO_ALL) {
|
||||
globalResponse[0] = OverwriteResponse.NO_TO_ALL;
|
||||
currentItem[0] += countItems(file);
|
||||
break;
|
||||
}
|
||||
if (res == OverwriteResponse.NO) {
|
||||
currentItem[0] += countItems(file);
|
||||
break;
|
||||
}
|
||||
if (res == OverwriteResponse.YES_TO_ALL)
|
||||
globalResponse[0] = OverwriteResponse.YES_TO_ALL;
|
||||
}
|
||||
}
|
||||
copyFileWithProgress(file, targetFile, totalItems, currentItem, callback);
|
||||
}
|
||||
if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) {
|
||||
OverwriteResponse res = callback.confirmOverwrite(file, targetFile);
|
||||
if (res == OverwriteResponse.CANCEL) return;
|
||||
if (res == OverwriteResponse.NO_TO_ALL) {
|
||||
globalResponse[0] = OverwriteResponse.NO_TO_ALL;
|
||||
currentItem[0] += countItems(file);
|
||||
continue;
|
||||
}
|
||||
if (res == OverwriteResponse.NO) {
|
||||
currentItem[0] += countItems(file);
|
||||
continue;
|
||||
}
|
||||
if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL;
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(file, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
|
||||
// SKIP: Break while(true) to continue with next file in for loop
|
||||
// we don't increment currentItem here because it's hard to know
|
||||
// what exactly was processed inside copyDirectory if it failed midway.
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
copyFileWithProgress(file, targetFile, totalItems, currentItem, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,6 +296,7 @@ public class FileOperations {
|
||||
long totalItems = calculateTotalItems(cleanedItems);
|
||||
long[] currentItem = {0};
|
||||
final OverwriteResponse[] globalResponse = {null};
|
||||
final ErrorResponse[] globalErrorResponse = {null};
|
||||
|
||||
for (FileItem item : cleanedItems) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
@ -306,6 +330,7 @@ public class FileOperations {
|
||||
|
||||
long itemCount = countItems(source);
|
||||
while (true) {
|
||||
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
|
||||
try {
|
||||
if (source.renameTo(target)) {
|
||||
currentItem[0] += itemCount;
|
||||
@ -315,7 +340,7 @@ public class FileOperations {
|
||||
} else {
|
||||
// Fallback for cross-device moves
|
||||
if (source.isDirectory()) {
|
||||
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse);
|
||||
copyDirectory(source, target, totalItems, currentItem, callback, globalResponse, globalErrorResponse);
|
||||
deleteDirectoryInternal(source);
|
||||
} else {
|
||||
copyFileWithProgress(source, target, totalItems, currentItem, callback);
|
||||
@ -328,6 +353,7 @@ public class FileOperations {
|
||||
ErrorResponse res = callback.onError(source, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
@ -348,15 +374,17 @@ public class FileOperations {
|
||||
List<FileItem> cleanedItems = cleanDuplicateItems(items);
|
||||
long totalItems = calculateTotalItems(cleanedItems);
|
||||
long[] currentItem = {0};
|
||||
final ErrorResponse[] globalErrorResponse = {null};
|
||||
|
||||
for (FileItem item : cleanedItems) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
File file = item.getFile();
|
||||
|
||||
while (true) {
|
||||
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
deleteDirectoryRecursive(file, totalItems, currentItem, callback);
|
||||
deleteDirectoryRecursive(file, totalItems, currentItem, callback, globalErrorResponse);
|
||||
} else {
|
||||
currentItem[0]++;
|
||||
if (callback != null) {
|
||||
@ -370,6 +398,7 @@ public class FileOperations {
|
||||
ErrorResponse res = callback.onError(file, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
@ -379,18 +408,36 @@ public class FileOperations {
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteDirectoryRecursive(File directory, long totalItems, long[] currentItem, ProgressCallback callback) throws IOException {
|
||||
private static void deleteDirectoryRecursive(File directory, long totalItems, long[] currentItem, ProgressCallback callback, final ErrorResponse[] globalErrorResponse) throws IOException {
|
||||
File[] children = directory.listFiles();
|
||||
if (children != null) {
|
||||
for (File child : children) {
|
||||
if (child.isDirectory()) {
|
||||
deleteDirectoryRecursive(child, totalItems, currentItem, callback);
|
||||
} else {
|
||||
currentItem[0]++;
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, child.getName());
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
if (globalErrorResponse[0] == ErrorResponse.SKIP_ALL) break;
|
||||
while (true) {
|
||||
try {
|
||||
if (child.isDirectory()) {
|
||||
deleteDirectoryRecursive(child, totalItems, currentItem, callback, globalErrorResponse);
|
||||
} else {
|
||||
currentItem[0]++;
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, child.getName());
|
||||
}
|
||||
deleteFileWithForce(child);
|
||||
}
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(child, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
if (res == ErrorResponse.SKIP_ALL) globalErrorResponse[0] = ErrorResponse.SKIP_ALL;
|
||||
// SKIP:
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
deleteFileWithForce(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -490,9 +537,23 @@ public class FileOperations {
|
||||
public static void deleteFromFtp(List<FileItem> items, ProgressCallback callback) throws IOException {
|
||||
for (FileItem item : items) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
FtpService.deleteOnFtp(item.getFtpProfile(), item.getFtpPath());
|
||||
if (callback != null) {
|
||||
callback.onFileProgress(item.getSize(), item.getSize());
|
||||
while (true) {
|
||||
try {
|
||||
FtpService.deleteOnFtp(item.getFtpProfile(), item.getFtpPath());
|
||||
if (callback != null) {
|
||||
callback.onFileProgress(item.getSize(), item.getSize());
|
||||
}
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(item.getFile(), e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -504,23 +565,37 @@ public class FileOperations {
|
||||
for (FileItem item : items) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
|
||||
if (item.isDirectory()) {
|
||||
if (item.isFtp()) {
|
||||
File tempDir = java.nio.file.Files.createTempDirectory("kf-ftp-copy").toFile();
|
||||
copyFromFtp(java.util.Collections.singletonList(item), tempDir, callback);
|
||||
uploadLocalToFtp(profile, tempDir, remotePath + "/" + item.getName(), callback);
|
||||
deleteRecursively(tempDir);
|
||||
} else {
|
||||
uploadLocalToFtp(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
|
||||
}
|
||||
} else {
|
||||
if (item.isFtp()) {
|
||||
File tempFile = java.nio.file.Files.createTempFile("kf-ftp-copy", ".tmp").toFile();
|
||||
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, callback);
|
||||
FtpService.uploadFile(profile, tempFile, remotePath + "/" + item.getName(), callback);
|
||||
tempFile.delete();
|
||||
} else {
|
||||
FtpService.uploadFile(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
|
||||
while (true) {
|
||||
try {
|
||||
if (item.isDirectory()) {
|
||||
if (item.isFtp()) {
|
||||
File tempDir = java.nio.file.Files.createTempDirectory("kf-ftp-copy").toFile();
|
||||
copyFromFtp(java.util.Collections.singletonList(item), tempDir, callback);
|
||||
uploadLocalToFtp(profile, tempDir, remotePath + "/" + item.getName(), callback);
|
||||
deleteRecursively(tempDir);
|
||||
} else {
|
||||
uploadLocalToFtp(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
|
||||
}
|
||||
} else {
|
||||
if (item.isFtp()) {
|
||||
File tempFile = java.nio.file.Files.createTempFile("kf-ftp-copy", ".tmp").toFile();
|
||||
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), tempFile, callback);
|
||||
FtpService.uploadFile(profile, tempFile, remotePath + "/" + item.getName(), callback);
|
||||
tempFile.delete();
|
||||
} else {
|
||||
FtpService.uploadFile(profile, item.getFile(), remotePath + "/" + item.getName(), callback);
|
||||
}
|
||||
}
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(item.getFile(), e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -543,22 +618,36 @@ public class FileOperations {
|
||||
|
||||
for (FileItem item : ftpItems) {
|
||||
if (callback != null && callback.isCancelled()) break;
|
||||
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, item.getName());
|
||||
}
|
||||
|
||||
if (item.isDirectory()) {
|
||||
File targetDir = new File(localTarget, item.getName());
|
||||
FtpService.downloadDirectory(item.getFtpProfile(), item.getFtpPath(), targetDir, callback);
|
||||
} else {
|
||||
File targetFile = new File(localTarget, item.getName());
|
||||
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), targetFile, callback);
|
||||
}
|
||||
|
||||
currentItem[0]++;
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, item.getName());
|
||||
while (true) {
|
||||
try {
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, item.getName());
|
||||
}
|
||||
|
||||
if (item.isDirectory()) {
|
||||
File targetDir = new File(localTarget, item.getName());
|
||||
FtpService.downloadDirectory(item.getFtpProfile(), item.getFtpPath(), targetDir, callback);
|
||||
} else {
|
||||
File targetFile = new File(localTarget, item.getName());
|
||||
FtpService.downloadFile(item.getFtpProfile(), item.getFtpPath(), targetFile, callback);
|
||||
}
|
||||
|
||||
currentItem[0]++;
|
||||
if (callback != null) {
|
||||
callback.onProgress(currentItem[0], totalItems, item.getName());
|
||||
}
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(item.getFile(), e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
if (res == ErrorResponse.RETRY) continue;
|
||||
break;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1635,7 +1724,7 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
public enum ErrorResponse {
|
||||
RETRY, SKIP, ABORT
|
||||
RETRY, SKIP, SKIP_ALL, ABORT
|
||||
}
|
||||
|
||||
public enum SymlinkResponse {
|
||||
|
||||
@ -3767,7 +3767,7 @@ public class MainWindow extends JFrame {
|
||||
final FileOperations.ErrorResponse[] result = new FileOperations.ErrorResponse[1];
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
Object[] options = {"Skip", "Retry", "Abort"};
|
||||
Object[] options = {"Skip", "Skip All", "Retry", "Abort"};
|
||||
int n = JOptionPane.showOptionDialog(progressDialog,
|
||||
"Error operating on file: " + file.getName() + "\n" + e.getMessage(),
|
||||
"Error",
|
||||
@ -3779,7 +3779,8 @@ public class MainWindow extends JFrame {
|
||||
|
||||
switch (n) {
|
||||
case 0: result[0] = FileOperations.ErrorResponse.SKIP; break;
|
||||
case 1: result[0] = FileOperations.ErrorResponse.RETRY; break;
|
||||
case 1: result[0] = FileOperations.ErrorResponse.SKIP_ALL; break;
|
||||
case 2: result[0] = FileOperations.ErrorResponse.RETRY; break;
|
||||
default:
|
||||
result[0] = FileOperations.ErrorResponse.ABORT;
|
||||
progressDialog.cancel();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user