iOS获取下载的M4A/MP3文件及重启后播放失效问题求助
我来帮你解决这个头疼的问题!你遇到的情况是iOS沙盒的一个典型特性导致的——应用重启(或更新)后,沙盒容器的UUID标识会发生变化,所以你之前保存的完整绝对URL(比如file:///var/mobile/Containers/Data/Application/EDF4365C-59B4-4FCA-9E35-BF4FCD667134/Library/Caches/SoundHelix-Song-1.mp3)就失效了,哪怕文件本身还在容器内的相对位置。
问题出在你的代码哪里?
你在下载完成后直接把destinationURL(完整绝对路径)赋值给了songURL,这个URL绑定了当前会话的容器UUID,一旦应用重启,容器ID变了,这个URL自然就找不到文件了。另外还有个小问题:你的getAudio方法里用的是.documentDirectory,但下载完成后却把文件存到了.libraryDirectory,目录不一致也可能导致后续判断文件是否存在出错。
具体解决方案
1. 只保存文件名(而非完整URL)
下载完成后,不要存储完整的绝对路径,只保存文件的名称即可,比如:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { print("downloadLocation:", location) guard let url = downloadTask.originalRequest?.url else { return } let fileName = url.lastPathComponent // 只提取文件名 let documentsPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] let destinationURL = documentsPath.appendingPathComponent(fileName) try? FileManager.default.removeItem(at: destinationURL) do { try FileManager.default.copyItem(at: location, to: destinationURL) self.savedAudioFileName = fileName // 保存文件名,而不是完整URL self.downloaded = true } catch let error { print("Copy Error: \(error.localizedDescription)") } }
这里需要给你的类添加一个属性来存储文件名:
var savedAudioFileName: String?
2. 每次需要访问文件时,重新构建当前有效的URL
当你需要播放音频时,不要直接用之前保存的URL,而是根据当前的沙盒目录和保存的文件名重新拼接:
func getValidAudioURL() -> URL? { guard let fileName = savedAudioFileName else { return nil } let currentLibraryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask)[0] return currentLibraryURL.appendingPathComponent(fileName) }
之后播放音频时,调用这个方法拿到最新的URL即可:
if let audioURL = getValidAudioURL(), FileManager.default.fileExists(atPath: audioURL.path) { // 在这里进行音频播放逻辑 }
3. 统一文件存储目录
修正getAudio方法里的目录,和下载时保持一致(都用.libraryDirectory):
func getAudio () { if let audioUrl = songURL { let libraryDirectoryURL = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first! let destinationUrl = libraryDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent) print(destinationUrl) if FileManager.default.fileExists(atPath: destinationUrl.path) { print("The file already exists at path") self.savedAudioFileName = audioUrl.lastPathComponent // 直接保存文件名 self.downloaded = true } else { let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue()) let downloadTask = urlSession.downloadTask(with: audioUrl) downloadTask.resume() } } }
为什么这样有效?
iOS应用的沙盒容器ID是动态变化的,但容器内的目录结构(比如Library/Caches)是固定的。只要你保存的是文件的相对位置(这里就是文件名),每次重新拼接当前的沙盒目录路径,就能得到永远有效的文件URL,不管应用重启多少次。
内容的提问来源于stack exchange,提问作者Aqib Javed

