chore: supports retaining file modification date

This commit is contained in:
hstyi
2025-07-01 10:39:08 +08:00
committed by hstyi
parent 472bf6e81f
commit eee016c643

View File

@@ -1,12 +1,19 @@
package app.termora.transfer package app.termora.transfer
import app.termora.database.DatabaseManager
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.apache.sshd.sftp.client.fs.SftpFileSystem
import org.apache.sshd.sftp.common.SftpConstants
import org.slf4j.LoggerFactory
import java.io.Closeable import java.io.Closeable
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardOpenOption import java.nio.file.StandardOpenOption
import java.nio.file.attribute.BasicFileAttributeView
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import kotlin.io.path.getLastModifiedTime
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
import kotlin.io.path.outputStream import kotlin.io.path.outputStream
@@ -15,10 +22,13 @@ class FileTransfer(
private val action: TransferAction, private val action: TransferAction,
priority: Transfer.Priority = Transfer.Priority.Normal, priority: Transfer.Priority = Transfer.Priority.Normal,
) : AbstractTransfer(parentId, source, target, false, priority), Closeable { ) : AbstractTransfer(parentId, source, target, false, priority), Closeable {
companion object {
private val log = LoggerFactory.getLogger(FileTransfer::class.java)
}
private lateinit var input: InputStream private lateinit var input: InputStream
private lateinit var output: OutputStream private lateinit var output: OutputStream
private val isPreserveModificationTime get() = DatabaseManager.getInstance().sftp.preserveModificationTime
private val closed = AtomicBoolean(false) private val closed = AtomicBoolean(false)
override suspend fun transfer(bufferSize: Int): Long { override suspend fun transfer(bufferSize: Int): Long {
@@ -31,7 +41,7 @@ class FileTransfer(
if (::output.isInitialized.not()) { if (::output.isInitialized.not()) {
output = if (action == TransferAction.Overwrite) { output = if (action == TransferAction.Overwrite) {
target().outputStream(StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING) target().outputStream()
} else { } else {
target().outputStream(StandardOpenOption.WRITE, StandardOpenOption.APPEND) target().outputStream(StandardOpenOption.WRITE, StandardOpenOption.APPEND)
} }
@@ -40,6 +50,7 @@ class FileTransfer(
val buffer = ByteArray(bufferSize) val buffer = ByteArray(bufferSize)
val len = input.read(buffer) val len = input.read(buffer)
if (len <= 0) return 0 if (len <= 0) return 0
output.write(buffer, 0, len) output.write(buffer, 0, len)
return len.toLong() return len.toLong()
} }
@@ -61,6 +72,24 @@ class FileTransfer(
if (::output.isInitialized) { if (::output.isInitialized) {
IOUtils.closeQuietly(output) IOUtils.closeQuietly(output)
} }
if (isPreserveModificationTime) {
runCatching {
val time = source().getLastModifiedTime()
val fs = target().fileSystem
// SFTP 比较特殊
if (fs is SftpFileSystem && fs.version == SftpConstants.SFTP_V3) {
val view = Files.getFileAttributeView(target(), BasicFileAttributeView::class.java)
view.setTimes(time, time, null)
} else {
Files.setLastModifiedTime(target(), time)
}
}.onFailure {
if (log.isWarnEnabled) {
log.error(it.message, it)
}
}
}
} }
} }