fix(iOS): read file data into memory BEFORE revoking security-scoped access, prevents exists=false
This commit is contained in:
parent
6503712be9
commit
3ed5696ae0
@ -771,36 +771,43 @@ final class PlayerBridge: ObservableObject {
|
|||||||
/// 处理 iOS 文件选择器结果
|
/// 处理 iOS 文件选择器结果
|
||||||
func handleFileImport(_ urls: [URL]) {
|
func handleFileImport(_ urls: [URL]) {
|
||||||
diagLog("handleFileImport: \(urls.count) URLs")
|
diagLog("handleFileImport: \(urls.count) URLs")
|
||||||
|
let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||||
|
|
||||||
for url in urls {
|
for url in urls {
|
||||||
diagLog("processing: \(url.lastPathComponent)")
|
diagLog("processing: \(url.lastPathComponent)")
|
||||||
let didStartAccessing = url.startAccessingSecurityScopedResource()
|
let didStartAccessing = url.startAccessingSecurityScopedResource()
|
||||||
diagLog("startAccessingSecurityScopedResource: \(didStartAccessing)")
|
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() }
|
if didStartAccessing { url.stopAccessingSecurityScopedResource() }
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 复制文件到 app Documents 目录(security-scoped URL 在 defer 后会失效)
|
// 撤销安全域访问(文件数据已在内存中)
|
||||||
let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
if didStartAccessing { url.stopAccessingSecurityScopedResource() }
|
||||||
let localURL = docsDir.appendingPathComponent(url.lastPathComponent)
|
|
||||||
|
// 写入 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 {
|
do {
|
||||||
// 如果同名文件已存在,加时间戳
|
try fileData.write(to: localURL)
|
||||||
if FileManager.default.fileExists(atPath: localURL.path) {
|
diagLog("written \(fileData.count) bytes to: \(localURL.lastPathComponent)")
|
||||||
let ts = Int(Date().timeIntervalSince1970)
|
addItem(url: localURL, name: url.lastPathComponent, type: "file")
|
||||||
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")
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
diagLog("copy failed: \(error.localizedDescription)")
|
diagLog("write failed: \(error.localizedDescription)")
|
||||||
addItem(url: url, name: url.lastPathComponent, type: "file")
|
showToast("Failed to import: \(url.lastPathComponent)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user