You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一FileLock下完成文件的读取与写入操作?

解决方案:同一FileLock下完成文件读写

你的问题根源在于Channels.newInputStream(channel)返回的输入流,调用close()时会直接关闭关联的FileChannel——而Kotlin的use块会自动执行流的close(),导致后续写入操作时通道已经失效。

不需要必须用RandomAccessFile,有两种更合适的方案:

方案一:包装流避免关闭通道

自定义包装类覆盖流的close()方法,不让它关闭底层通道:

// 自定义不关闭底层通道的输入流
class NoCloseInputStream(delegate: InputStream) : FilterInputStream(delegate) {
    override fun close() {
        // 空实现,不关闭底层通道
    }
}

// 自定义不关闭底层通道的输出流
class NoCloseOutputStream(delegate: OutputStream) : FilterOutputStream(delegate) {
    override fun close() {
        flush() // 只刷新,不关闭底层通道
    }
}

修改业务代码,使用包装流处理读写,同时注意重置通道位置和截断文件:

val storeChannel =
    FileChannel.open(
        target,
        StandardOpenOption.READ,
        StandardOpenOption.WRITE,
        StandardOpenOption.CREATE
    )
return runLocking(storeChannel) { channel ->
    // 读取数据:使用包装流,use块不会关闭通道
    val inStream = NoCloseInputStream(Channels.newInputStream(channel))
    val store = inStream.use { serializer.read(it) }
    
    val result = operation(store)
    
    // 重置通道位置到文件开头,避免追加写入
    channel.position(0)
    
    // 写入数据:同样使用包装流
    val outStream = NoCloseOutputStream(Channels.newOutputStream(channel))
    outStream.use { serializer.write(it, store) }
    
    // 截断文件到当前写入位置,避免旧数据残留(如果新数据比原文件短)
    channel.truncate(channel.position())
    
    result
}

方案二:直接使用FileChannel API读写

如果你的序列化工具支持直接处理字节数据,可以跳过InputStream/OutputStream,直接用FileChannel的读写方法:

val storeChannel =
    FileChannel.open(
        target,
        StandardOpenOption.READ,
        StandardOpenOption.WRITE,
        StandardOpenOption.CREATE
    )
return runLocking(storeChannel) { channel ->
    // 读取文件到ByteBuffer
    val fileSize = channel.size()
    val readBuffer = ByteBuffer.allocate(fileSize.toInt())
    channel.read(readBuffer)
    readBuffer.flip() // 切换为读模式
    
    // 反序列化:将ByteBuffer转成InputStream给序列化工具
    val store = serializer.read(ByteArrayInputStream(readBuffer.array()))
    
    val result = operation(store)
    
    // 重置通道位置到开头
    channel.position(0)
    
    // 序列化到字节数组,再写入通道
    val baos = ByteArrayOutputStream()
    serializer.write(baos, store)
    val writeBuffer = ByteBuffer.wrap(baos.toByteArray())
    channel.write(writeBuffer)
    
    // 截断文件到新数据长度
    channel.truncate(baos.size().toLong())
    
    result
}

关键注意点

  1. 读取完成后必须重置通道位置到0,否则写入会在文件末尾追加而不是覆盖。
  2. 写入完成后调用channel.truncate(channel.position()),确保原文件中超出新数据长度的部分被删除,避免残留旧数据。

内容的提问来源于stack exchange,提问作者Ian Rehwinkel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 22:46:32