@@ -3,6 +3,20 @@ package me.gegenbauer.catspy.log.ui.tab
33import info.clearthought.layout.TableLayout
44import info.clearthought.layout.TableLayoutConstants.FILL
55import info.clearthought.layout.TableLayoutConstants.PREFERRED
6+ import kotlinx.coroutines.CoroutineScope
7+ import kotlinx.coroutines.Dispatchers
8+ import kotlinx.coroutines.GlobalScope
9+ import kotlinx.coroutines.async
10+ import kotlinx.coroutines.awaitAll
11+ import kotlinx.coroutines.channels.Channel
12+ import kotlinx.coroutines.coroutineScope
13+ import kotlinx.coroutines.delay
14+ import kotlinx.coroutines.flow.flow
15+ import kotlinx.coroutines.flow.onCompletion
16+ import kotlinx.coroutines.flow.onStart
17+ import kotlinx.coroutines.launch
18+ import kotlinx.coroutines.runBlocking
19+ import kotlinx.coroutines.withContext
620import me.gegenbauer.catspy.file.toHumanReadableSize
721import me.gegenbauer.catspy.iconset.GIcons
822import me.gegenbauer.catspy.java.ext.EMPTY_STRING
@@ -26,6 +40,7 @@ import java.awt.event.ActionListener
2640import java.awt.event.MouseAdapter
2741import java.awt.event.MouseEvent
2842import java.io.File
43+ import java.nio.charset.Charset
2944import java.text.SimpleDateFormat
3045import java.util.*
3146import javax.swing.BorderFactory
@@ -76,16 +91,93 @@ class FileLogGuidancePanel(
7691 recentFilesPanel.setFiles(files)
7792 }
7893
94+ // 核心协程版实现
95+ private suspend fun mergeFilesWithCoroutine (
96+ sourceFiles : List <File >,
97+ targetFile : File ,
98+ startMarkerTemplate : String = "── %s START ──", // 允许自定义标记格式
99+ endMarkerTemplate : String = "── %s END ──",
100+ markerCharset : String = "UTF -8"
101+ ) = coroutineScope {
102+ require(sourceFiles.isNotEmpty()) { " Source files cannot be empty" }
103+
104+ // 准备目标文件
105+ targetFile.apply {
106+ parentFile?.mkdirs()
107+ delete()
108+ }
109+
110+ val channel = Channel <ByteArray >(capacity = 1024 )
111+ val charset = Charset .forName(markerCharset)
112+
113+ // 启动单个写入协程
114+ val writerJob = launch(Dispatchers .IO ) {
115+ targetFile.outputStream().buffered().use { os ->
116+ for (bytes in channel) {
117+ os.write(bytes)
118+ }
119+ }
120+ }
121+
122+ sourceFiles.asReversed().forEach { file ->
123+ launch(Dispatchers .IO ) {
124+ try {
125+ // 写入开始标记
126+ val startMarker = startMarkerTemplate.format(file.name)
127+ channel.send(startMarker.toByteArray(charset))
128+ channel.send(" \n " .toByteArray(charset))
129+
130+ // 写入文件内容
131+ file.inputStream().buffered().use { ins ->
132+ val buffer = ByteArray (8192 )
133+ var bytesRead: Int
134+ while (ins.read(buffer).also { bytesRead = it } != - 1 ) {
135+ channel.send(buffer.copyOf(bytesRead))
136+ }
137+ }
138+
139+ // 写入结束标记
140+ val endMarker = endMarkerTemplate.format(file.name)
141+ channel.send(" \n " .toByteArray(charset))
142+ channel.send(endMarker.toByteArray(charset))
143+ channel.send(" \n\n " .toByteArray(charset)) // 文件间空行
144+ } catch (e: Exception ) {
145+ // 错误标记
146+ channel.send(" [ERROR processing ${file.name} : ${e.message} ]" .toByteArray(charset))
147+ throw e
148+ }
149+ }.join() // 保证文件顺序处理
150+ }
151+
152+ channel.close()
153+ writerJob.join()
154+ }
155+
79156 private fun onClickFileOpen () {
80157 val files = showSelectSingleFileDialog(
81158 findFrameFromParent(),
82159 STRINGS .ui.openFile,
83160 RecentLogFiles .getLastOpenDir(),
84161 )
85- files.firstOrNull()?.let {
86- onOpenFile(it)
87- RecentLogFiles .onNewFileOpen(it.absolutePath)
162+ runBlocking {
163+ var openFile = File (files.first().absolutePath)
164+ if (files.size >= 2 ){
165+ // 获取files第一个文件的地址
166+ openFile = File (files.first().parentFile.absolutePath+ File .separator+ " merge.log" )
167+
168+ val mergeJob = GlobalScope .launch(Dispatchers .IO ) {
169+ mergeFilesWithCoroutine(files, openFile)
170+ }
171+ mergeJob.join()
172+ print (" >>> after last file size:" + openFile.length())
173+ }
174+
175+ files.firstOrNull()?.let {
176+ onOpenFile(openFile)
177+ RecentLogFiles .onNewFileOpen(openFile.absolutePath)
178+ }
88179 }
180+
89181 }
90182
91183 private inner class RecentFilesPanel : ScrollConstrainedScrollablePanel (
0 commit comments