@@ -126,13 +126,21 @@ class ResticRepository @Inject constructor(
126126 password : String ,
127127 snapshotId : String ,
128128 targetPath : String ,
129+ snapshotSubPath : String? = null,
129130 includePath : String? = null,
130131 progressCallback : ResticProgressCallback ? = null
131132 ): Boolean {
132133 return withContext(Dispatchers .IO ) {
133134 try {
135+ // 构造带子路径的快照ID
136+ val fullSnapshotId = if (! snapshotSubPath.isNullOrEmpty()) {
137+ " $snapshotId :$snapshotSubPath "
138+ } else {
139+ snapshotId
140+ }
141+
134142 val args = mutableListOf (
135- resticPath, " restore" , snapshotId ,
143+ resticPath, " restore" , fullSnapshotId ,
136144 " --repo" , repoPath,
137145 " --target" , targetPath,
138146 " --json"
@@ -152,56 +160,117 @@ class ResticRepository @Inject constructor(
152160 processBuilder.environment()[" XDG_CACHE_HOME" ] = File (context.cacheDir, " restic" ).absolutePath
153161 val process = processBuilder.start()
154162
155- // 分别读取标准输出和错误输出
156- val stdout = process.inputStream.bufferedReader()
157- val stderr = process.errorStream.bufferedReader()
158-
159- // 处理标准输出(进度信息)
160- stdout.use { reader ->
161- reader.forEachLine { line ->
162- try {
163- val progress = parseRestoreProgress(line)
164- if (progress != null && progressCallback != null ) {
165- progressCallback.onProgress(
166- filesFinished = progress.files_finished ? : 0 ,
167- filesTotal = progress.files_total ? : 0 ,
168- bytesWritten = progress.bytes_written ? : 0 ,
169- bytesTotal = progress.bytes_total ? : 0 ,
170- filesSkipped = progress.files_skipped ? : 0 ,
171- bytesSkipped = progress.bytes_skipped ? : 0
172- )
163+ try {
164+ // 分别读取标准输出和错误输出
165+ val stdout = process.inputStream.bufferedReader()
166+ val stderr = process.errorStream.bufferedReader()
167+
168+ // 处理标准输出(进度信息)
169+ val outputBuilder = StringBuilder ()
170+ stdout.use { reader ->
171+ reader.forEachLine { line ->
172+ outputBuilder.appendLine(line) // 捕获输出
173+
174+ try {
175+ val progress = parseRestoreProgress(line)
176+ if (progress != null && progressCallback != null ) {
177+ progressCallback.onProgress(
178+ filesFinished = progress.files_finished ? : 0 ,
179+ filesTotal = progress.files_total ? : 0 ,
180+ bytesWritten = progress.bytes_written ? : 0 ,
181+ bytesTotal = progress.bytes_total ? : 0 ,
182+ filesSkipped = progress.files_skipped ? : 0 ,
183+ bytesSkipped = progress.bytes_skipped ? : 0
184+ )
185+ }
186+ } catch (e: Exception ) {
187+ Log .w(TAG , " Failed to parse progress line: $line " , e)
173188 }
174- } catch (e: Exception ) {
175- Log .w(TAG , " Failed to parse progress line: $line " , e)
176189 }
177190 }
178- }
179191
180- // 读取错误输出用于调试
181- val errorOutput = stderr.readText()
182- if (errorOutput.isNotEmpty()) {
183- Log .e(TAG , " Restic restore error: $errorOutput " )
184- }
185- val output = stdout.readText()
192+ // 读取错误输出用于调试
193+ val errorOutput = stderr.readText()
194+ val output = outputBuilder.toString() // 使用捕获的输出
186195
187- val exitCode = process.waitFor()
196+ val exitCode = process.waitFor()
188197
189- // 添加详细日志
190- Log .d(TAG , " Restic restore exit code: $exitCode " )
191- if (exitCode != 0 ) {
192- Log .e (TAG , " Restic restore error output : $errorOutput " )
193- }
194- Log .d(TAG , " Restic restore output : $output " )
198+ // === 详细恢复过程诊断 ===
199+ Log .d(TAG , " ========== Restic 恢复过程详情 ========== " )
200+ Log .d( TAG , " 命令: ${args.joinToString( " " )} " )
201+ Log .d (TAG , " 退出码 : $exitCode " )
202+ Log .d( TAG , " 标准输出长度: ${output.length} 字符 " )
203+ Log .d(TAG , " 错误输出长度 : ${errorOutput.length} 字符 " )
195204
196- logger.logCommandResult(exitCode, output)
197- exitCode == 0
205+ if (output.isNotEmpty()) {
206+ Log .d(TAG , " === 标准输出内容 ===" )
207+ output.lines().forEachIndexed { index, line ->
208+ Log .d(TAG , " stdout[$index ]: $line " )
209+ }
210+ }
211+
212+ if (errorOutput.isNotEmpty()) {
213+ Log .e(TAG , " === 错误输出内容 ===" )
214+ errorOutput.lines().forEachIndexed { index, line ->
215+ Log .e(TAG , " stderr[$index ]: $line " )
216+ }
217+ }
218+
219+ // 分析退出码原因
220+ if (exitCode != 0 ) {
221+ Log .w(TAG , " === 退出码分析 ===" )
222+ when {
223+ errorOutput.contains(" warning" , ignoreCase = true ) -> {
224+ Log .w(TAG , " ✓ 检测到警告信息" )
225+ }
226+ errorOutput.contains(" error" , ignoreCase = true ) -> {
227+ Log .e(TAG , " ✗ 检测到错误信息" )
228+ }
229+ errorOutput.contains(" file not found" , ignoreCase = true ) -> {
230+ Log .e(TAG , " ✗ 文件未找到" )
231+ }
232+ errorOutput.contains(" permission denied" , ignoreCase = true ) -> {
233+ Log .e(TAG , " ✗ 权限被拒绝" )
234+ }
235+ output.contains(" restoring" , ignoreCase = true ) -> {
236+ Log .w(TAG , " ✓ 检测到恢复操作记录" )
237+ }
238+ else -> {
239+ Log .w(TAG , " ? 未知原因的非零退出码" )
240+ }
241+ }
242+
243+ // 检查目标文件
244+ val targetFile = File (targetPath, includePath ? : " " )
245+ Log .w(TAG , " 目标文件检查: ${targetFile.absolutePath} " )
246+ Log .w(TAG , " 文件存在: ${targetFile.exists()} " )
247+ if (targetFile.exists()) {
248+ Log .w(TAG , " 文件大小: ${targetFile.length()} bytes" )
249+ Log .w(TAG , " 文件修改时间: ${targetFile.lastModified()} " )
250+ }
251+ }
252+
253+ Log .d(TAG , " ========================================" )
254+
255+ logger.logCommandResult(exitCode, output)
256+ exitCode == 0
257+ } catch (e: Exception ) {
258+ Log .e(TAG , " Restic restore process exception" , e)
259+ // 尝试获取可能的错误输出
260+ try {
261+ val errorOutput = process.errorStream.bufferedReader().readText()
262+ Log .e(TAG , " Process error output: $errorOutput " )
263+ } catch (ex: Exception ) {
264+ Log .e(TAG , " Failed to read error output" , ex)
265+ }
266+ throw e
267+ }
198268 } catch (e: Exception ) {
199269 logger.logCommandFailed(e)
200270 false
201271 }
202272 }
203273 }
204-
205274 // 添加进度解析方法
206275 private fun parseRestoreProgress (line : String ): ResticRestoreProgress ? {
207276 return try {
0 commit comments