From 1516d6d81ed5e4f19a9f637dae598dad84653c30 Mon Sep 17 00:00:00 2001 From: hstyi Date: Sat, 29 Mar 2025 14:29:13 +0800 Subject: [PATCH] fix: SFTP add transport NPE --- .../app/termora/sftp/FileSystemViewTable.kt | 117 +++++++++--------- .../app/termora/sftp/TransportTableModel.kt | 15 ++- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/main/kotlin/app/termora/sftp/FileSystemViewTable.kt b/src/main/kotlin/app/termora/sftp/FileSystemViewTable.kt index 87a58bc..857bac1 100644 --- a/src/main/kotlin/app/termora/sftp/FileSystemViewTable.kt +++ b/src/main/kotlin/app/termora/sftp/FileSystemViewTable.kt @@ -680,7 +680,7 @@ class FileSystemViewTable( coroutineScope.launch { try { - doTransfer(arrayOf(attr), fromLocalSystem, targetWorkdir) + doTransfer(attr, fromLocalSystem, targetWorkdir) } catch (e: Exception) { if (log.isErrorEnabled) { log.error(e.message, e) @@ -829,78 +829,69 @@ class FileSystemViewTable( * 开始查找所有子,查找到之后立即添加任务,如果添加失败(任意一个)那么立即终止 */ private fun doTransfer( - attrs: Array, + attr: FileSystemViewTableModel.Attr, fromLocalSystem: Boolean, targetWorkdir: Path? ) { - if (attrs.isEmpty()) return val sftpPanel = this.sftpPanel val target = sftpPanel.getTarget(table) ?: return - var isTerminate = false + + /** + * 定义一个添加器,它可以自动的判断导入/拖拽行为 + */ + val adder = object { + fun add(transport: Transport): Boolean { + return addTransport( + sftpPanel, + if (fromLocalSystem) attr.path.parent else null, + target, + targetWorkdir, + transport + ) + } + } + + if (attr.isFile) { + if (!adder.add(createTransport(attr.path, false, 0).apply { scanned() })) { + return + } + } + val queue = ArrayDeque() + var isTerminate = false - for (attr in attrs) { - - /** - * 定义一个添加器,它可以自动的判断导入/拖拽行为 - */ - val adder = object { - fun add(transport: Transport): Boolean { - return addTransport( - sftpPanel, - if (fromLocalSystem) attr.path.parent else null, - target, - targetWorkdir, - transport - ) + try { + walk(attr.path, object : FileVisitor { + override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { + val transport = createTransport(dir, true, queue.lastOrNull()?.id ?: 0L) + .apply { queue.addLast(this) } + if (adder.add(transport)) return FileVisitResult.CONTINUE + return FileVisitResult.TERMINATE.apply { isTerminate = true } } - } - if (attr.isFile) { - if (!adder.add(createTransport(attr.path, false, 0).apply { scanned() })) { - isTerminate = true - break + override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { + if (queue.isEmpty()) return FileVisitResult.SKIP_SIBLINGS + val transport = createTransport(file, false, queue.last().id).apply { scanned() } + if (adder.add(transport)) return FileVisitResult.CONTINUE + return FileVisitResult.TERMINATE.apply { isTerminate = true } } - continue - } - queue.clear() - - try { - walk(attr.path, object : FileVisitor { - override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { - val transport = createTransport(dir, true, queue.lastOrNull()?.id ?: 0L) - .apply { queue.addLast(this) } - if (adder.add(transport)) return FileVisitResult.CONTINUE - return FileVisitResult.TERMINATE.apply { isTerminate = true } - } - - override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { - if (queue.isEmpty()) return FileVisitResult.SKIP_SIBLINGS - val transport = createTransport(file, false, queue.last().id).apply { scanned() } - if (adder.add(transport)) return FileVisitResult.CONTINUE - return FileVisitResult.TERMINATE.apply { isTerminate = true } - } - - override fun visitFileFailed(file: Path, exc: IOException): FileVisitResult { - return FileVisitResult.CONTINUE - } - - override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { - // 标记为扫描完毕 - queue.removeLast().scanned() - return FileVisitResult.CONTINUE - } - - }) - } catch (e: Exception) { - if (log.isErrorEnabled) { - log.error(e.message, e) + override fun visitFileFailed(file: Path, exc: IOException): FileVisitResult { + return FileVisitResult.CONTINUE } - isTerminate = true - } - if (isTerminate) break + override fun postVisitDirectory(dir: Path, exc: IOException?): FileVisitResult { + // 标记为扫描完毕 + queue.removeLast().scanned() + return FileVisitResult.CONTINUE + } + + }) + } catch (e: Exception) { + if (log.isErrorEnabled) { + log.error(e.message, e) + } + isTerminate = true } if (isTerminate) { @@ -960,7 +951,11 @@ class FileSystemViewTable( targetWorkdir: Path?, transport: Transport ): Boolean { - return sftpPanel.addTransport(table, sourceWorkdir, target, targetWorkdir, transport) + return try { + sftpPanel.addTransport(table, sourceWorkdir, target, targetWorkdir, transport) + } catch (e: Exception) { + false + } } private fun createTransport(source: Path, isDirectory: Boolean, parentId: Long): Transport { diff --git a/src/main/kotlin/app/termora/sftp/TransportTableModel.kt b/src/main/kotlin/app/termora/sftp/TransportTableModel.kt index 5d9a92a..60b622f 100644 --- a/src/main/kotlin/app/termora/sftp/TransportTableModel.kt +++ b/src/main/kotlin/app/termora/sftp/TransportTableModel.kt @@ -12,8 +12,10 @@ import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode import org.jdesktop.swingx.treetable.DefaultTreeTableModel import org.jdesktop.swingx.treetable.MutableTreeTableNode import org.slf4j.LoggerFactory +import java.util.* import java.util.concurrent.locks.ReentrantLock import javax.swing.SwingUtilities +import kotlin.collections.ArrayDeque import kotlin.io.path.name import kotlin.math.abs import kotlin.math.max @@ -27,7 +29,7 @@ class TransportTableModel(private val coroutineScope: CoroutineScope) : val lock = ReentrantLock() - private val transports = linkedMapOf() + private val transports = Collections.synchronizedMap(linkedMapOf()) private val reporter = SpeedReporter(coroutineScope) private var listeners = emptyArray() private val activeTransports = linkedMapOf() @@ -104,8 +106,15 @@ class TransportTableModel(private val coroutineScope: CoroutineScope) : transports[transport.id] = newNode if ((transports.containsKey(parentId) || p == root) && transports.containsKey(transport.id)) { - // 同步加入节点 - SwingUtilities.invokeLater { insertNodeInto(newNode, p, p.childCount) } + // 主线程加入节点 + SwingUtilities.invokeLater { + // 因为是异步的,父节点此时可能已经被移除了 + if (p == root || transports.containsKey(parentId)) { + insertNodeInto(newNode, p, p.childCount) + } else { + removeTransport(transport.id) + } + } } return@withLock true