From 919c06779da4d3ad8246c96e4c9bc55acd52de37 Mon Sep 17 00:00:00 2001 From: hstyi Date: Fri, 4 Jul 2025 15:27:09 +0800 Subject: [PATCH] chore: improve rm -rf --- .../app/termora/transfer/CommandTransfer.kt | 34 +++++++++ .../DefaultInternalTransferManager.kt | 72 +++++++++++-------- .../transfer/InternalTransferManager.kt | 1 + .../termora/transfer/TransferTreeTableNode.kt | 18 ++--- .../app/termora/transfer/TransportPanel.kt | 13 +--- 5 files changed, 88 insertions(+), 50 deletions(-) create mode 100644 src/main/kotlin/app/termora/transfer/CommandTransfer.kt diff --git a/src/main/kotlin/app/termora/transfer/CommandTransfer.kt b/src/main/kotlin/app/termora/transfer/CommandTransfer.kt new file mode 100644 index 0000000..f6b3fd7 --- /dev/null +++ b/src/main/kotlin/app/termora/transfer/CommandTransfer.kt @@ -0,0 +1,34 @@ +package app.termora.transfer + +import org.apache.sshd.sftp.client.fs.SftpFileSystem +import org.apache.sshd.sftp.client.fs.SftpPath +import kotlin.math.max + + +class CommandTransfer( + parentId: String, + path: SftpPath, + isDirectory: Boolean, + private val size: Long, + val command: String, +) : AbstractTransfer(parentId, path, path, isDirectory) { + + private var executed = false + + override suspend fun transfer(bufferSize: Int): Long { + if (executed) return 0 + val fs = source().fileSystem as SftpFileSystem + fs.session.executeRemoteCommand(command) + executed = true + return this.size() + } + + override fun scanning(): Boolean { + return false + } + + override fun size(): Long { + return max(size, 1) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/app/termora/transfer/DefaultInternalTransferManager.kt b/src/main/kotlin/app/termora/transfer/DefaultInternalTransferManager.kt index 6f17546..652b5f6 100644 --- a/src/main/kotlin/app/termora/transfer/DefaultInternalTransferManager.kt +++ b/src/main/kotlin/app/termora/transfer/DefaultInternalTransferManager.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.time.DateFormatUtils +import org.apache.sshd.sftp.client.fs.SftpPath import org.slf4j.LoggerFactory import java.awt.Component import java.awt.Dimension @@ -31,6 +32,7 @@ import kotlin.collections.ArrayDeque import kotlin.collections.List import kotlin.collections.Set import kotlin.collections.isNotEmpty +import kotlin.io.path.absolutePathString import kotlin.io.path.exists import kotlin.io.path.name import kotlin.io.path.pathString @@ -267,8 +269,8 @@ class DefaultInternalTransferManager( val isDirectory = pair.second.isDirectory val path = pair.first - if (isDirectory.not()) { - val transfer = createTransfer(path, workdir.resolve(path.name), false, StringUtils.EMPTY, mode, action) + if (isDirectory.not() || mode == TransferMode.Rmrf) { + val transfer = createTransfer(path, workdir.resolve(path.name), isDirectory, StringUtils.EMPTY, mode, action) return if (transferManager.addTransfer(transfer)) FileVisitResult.CONTINUE else FileVisitResult.TERMINATE } @@ -363,39 +365,49 @@ class DefaultInternalTransferManager( action:TransferAction, permissions: Set? = null ): Transfer { - if (mode == TransferMode.Delete) { - return DeleteTransfer( - parentId, - source, - isDirectory, - if (isDirectory) 1 else Files.size(source) - ) - } else if (mode == TransferMode.ChangePermission) { - if (permissions == null) throw IllegalStateException() - return ChangePermissionTransfer( - parentId, - target, - isDirectory = isDirectory, - permissions = permissions, - size = if (isDirectory) 1 else Files.size(target) - ) - } - - if (isDirectory) { - return DirectoryTransfer( + when { + mode == TransferMode.Delete -> { + return DeleteTransfer( + parentId, + source, + isDirectory, + if (isDirectory) 1 else Files.size(source) + ) + } + mode == TransferMode.Rmrf -> { + return CommandTransfer( + parentId, + source as SftpPath, + isDirectory, + if (isDirectory) 1 else Files.size(source), + "rm -rf ${source.absolutePathString()}", + ) + } + mode == TransferMode.ChangePermission -> { + if (permissions == null) throw IllegalStateException() + return ChangePermissionTransfer( + parentId, + target, + isDirectory = isDirectory, + permissions = permissions, + size = if (isDirectory) 1 else Files.size(target) + ) + } + isDirectory -> { + return DirectoryTransfer( + parentId = parentId, + source = source, + target = target, + ) + } + else -> return FileTransfer( parentId = parentId, source = source, target = target, + action = action, + size = Files.size(source) ) } - - return FileTransfer( - parentId = parentId, - source = source, - target = target, - action = action, - size = Files.size(source) - ) } diff --git a/src/main/kotlin/app/termora/transfer/InternalTransferManager.kt b/src/main/kotlin/app/termora/transfer/InternalTransferManager.kt index 9a3a8fc..176be3d 100644 --- a/src/main/kotlin/app/termora/transfer/InternalTransferManager.kt +++ b/src/main/kotlin/app/termora/transfer/InternalTransferManager.kt @@ -9,6 +9,7 @@ interface InternalTransferManager { Delete, Transfer, ChangePermission, + Rmrf, } /** diff --git a/src/main/kotlin/app/termora/transfer/TransferTreeTableNode.kt b/src/main/kotlin/app/termora/transfer/TransferTreeTableNode.kt index dc3992b..e9d1ac3 100644 --- a/src/main/kotlin/app/termora/transfer/TransferTreeTableNode.kt +++ b/src/main/kotlin/app/termora/transfer/TransferTreeTableNode.kt @@ -77,13 +77,15 @@ class TransferTreeTableNode(transfer: Transfer) : DefaultMutableTreeTableNode(tr private fun formatPath(path: Path, target: Boolean): String { if (target) { - if (transfer is DeleteTransfer) { - return I18n.getString("termora.transport.sftp.status.deleting") - } else if (transfer is ChangePermissionTransfer) { - val permissions = (transfer as ChangePermissionTransfer).permissions - // @formatter:off - return "${I18n.getString("termora.transport.table.permissions")} -> ${PosixFilePermissions.toString(permissions)}" - // @formatter:on + when (transfer) { + is DeleteTransfer -> return I18n.getString("termora.transport.sftp.status.deleting") + is CommandTransfer -> return (transfer as CommandTransfer).command + is ChangePermissionTransfer -> { + val permissions = (transfer as ChangePermissionTransfer).permissions + // @formatter:off + return "${I18n.getString("termora.transport.table.permissions")} -> ${PosixFilePermissions.toString(permissions)}" + // @formatter:on + } } } @@ -95,7 +97,7 @@ class TransferTreeTableNode(transfer: Transfer) : DefaultMutableTreeTableNode(tr } private fun formatStatus(state: State): String { - if (transfer is DeleteTransfer && state == State.Processing) { + if ((transfer is DeleteTransfer || transfer is CommandTransfer) && state == State.Processing) { return I18n.getString("termora.transport.sftp.status.deleting") } diff --git a/src/main/kotlin/app/termora/transfer/TransportPanel.kt b/src/main/kotlin/app/termora/transfer/TransportPanel.kt index 8e3dfef..7924890 100644 --- a/src/main/kotlin/app/termora/transfer/TransportPanel.kt +++ b/src/main/kotlin/app/termora/transfer/TransportPanel.kt @@ -18,7 +18,6 @@ import org.apache.commons.lang3.ArrayUtils import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.commons.lang3.time.DateFormatUtils -import org.apache.sshd.sftp.client.fs.SftpFileSystem import org.apache.sshd.sftp.client.fs.WithFileAttributes import org.jdesktop.swingx.JXBusyLabel import org.jdesktop.swingx.JXPanel @@ -34,7 +33,6 @@ import java.awt.event.* import java.beans.PropertyChangeEvent import java.beans.PropertyChangeListener import java.io.File -import java.io.OutputStream import java.nio.file.FileSystem import java.nio.file.FileSystems import java.nio.file.Files @@ -1023,16 +1021,7 @@ class TransportPanel( val target = source.parent.resolve(e.source.toString()) processPath(e.source.toString()) { source.moveTo(target) } } else if (actionCommand == TransportPopupMenu.ActionCommand.Rmrf) { - processPath(StringUtils.EMPTY) { - val session = (_fileSystem as SftpFileSystem).clientSession - for (path in files.map { it.first }) { - session.executeRemoteCommand( - "rm -rf '${path.absolutePathString()}'", - OutputStream.nullOutputStream(), - Charsets.UTF_8 - ) - } - } + transferManager.addTransfer(files, InternalTransferManager.TransferMode.Rmrf) } else if (actionCommand == TransportPopupMenu.ActionCommand.ChangePermissions) { val c = e.source as TransportPopupMenu.ChangePermission val path = files.first().first