From 3ed5696ae00aeda2d9be41133161fe74d287926c Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 3 Jul 2026 08:22:14 +0800 Subject: [PATCH] fix(iOS): read file data into memory BEFORE revoking security-scoped access, prevents exists=false --- Sources/PlayerBridge.swift | 47 ++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/Sources/PlayerBridge.swift b/Sources/PlayerBridge.swift index 09df647..fc6e73d 100644 --- a/Sources/PlayerBridge.swift +++ b/Sources/PlayerBridge.swift @@ -771,36 +771,43 @@ final class PlayerBridge: ObservableObject { /// 处理 iOS 文件选择器结果 func handleFileImport(_ urls: [URL]) { diagLog("handleFileImport: \(urls.count) URLs") + let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] + for url in urls { diagLog("processing: \(url.lastPathComponent)") let didStartAccessing = url.startAccessingSecurityScopedResource() diagLog("startAccessingSecurityScopedResource: \(didStartAccessing)") - defer { + + // 先把文件数据读到内存(安全域访问有效期内) + let fileData: Data + do { + fileData = try Data(contentsOf: url) + diagLog("read \(fileData.count) bytes from source") + } catch { + diagLog("read failed: \(error.localizedDescription)") if didStartAccessing { url.stopAccessingSecurityScopedResource() } + continue } - // 复制文件到 app Documents 目录(security-scoped URL 在 defer 后会失效) - let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - let localURL = docsDir.appendingPathComponent(url.lastPathComponent) + // 撤销安全域访问(文件数据已在内存中) + if didStartAccessing { url.stopAccessingSecurityScopedResource() } + + // 写入 Documents,确保用唯一的文件名 + let baseName = url.deletingPathExtension().lastPathComponent + let ext = url.pathExtension + var localURL = docsDir.appendingPathComponent(url.lastPathComponent) + if FileManager.default.fileExists(atPath: localURL.path) { + let ts = Int(Date().timeIntervalSince1970) + localURL = docsDir.appendingPathComponent("\(baseName)_\(ts).\(ext)") + } do { - // 如果同名文件已存在,加时间戳 - if FileManager.default.fileExists(atPath: localURL.path) { - let ts = Int(Date().timeIntervalSince1970) - let nameWithoutExt = url.deletingPathExtension().lastPathComponent - let ext = url.pathExtension - let newURL = docsDir.appendingPathComponent("\(nameWithoutExt)_\(ts).\(ext)") - try FileManager.default.copyItem(at: url, to: newURL) - diagLog("copied to: \(newURL.lastPathComponent)") - addItem(url: newURL, name: url.lastPathComponent, type: "file") - } else { - try FileManager.default.copyItem(at: url, to: localURL) - diagLog("copied to: \(localURL.lastPathComponent)") - addItem(url: localURL, name: url.lastPathComponent, type: "file") - } + try fileData.write(to: localURL) + diagLog("written \(fileData.count) bytes to: \(localURL.lastPathComponent)") + addItem(url: localURL, name: url.lastPathComponent, type: "file") } catch { - diagLog("copy failed: \(error.localizedDescription)") - addItem(url: url, name: url.lastPathComponent, type: "file") + diagLog("write failed: \(error.localizedDescription)") + showToast("Failed to import: \(url.lastPathComponent)") } } }