如何在同一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 }
关键注意点
- 读取完成后必须重置通道位置到
0,否则写入会在文件末尾追加而不是覆盖。 - 写入完成后调用
channel.truncate(channel.position()),确保原文件中超出新数据长度的部分被删除,避免残留旧数据。
内容的提问来源于stack exchange,提问作者Ian Rehwinkel
相关产品推荐
相关产品推荐

