iOS音频下载存文件后无法播放:代码及权限问题排查
问题排查与修复方案
1. 核心问题:AVPlayer 被立即释放
你在playAudio方法里创建的AVPlayer是局部变量,DispatchQueue.main.async闭包执行完毕后,这个实例会被系统立即回收,根本来不及启动播放。
修复方法:在ViewController类中声明全局属性强持有AVPlayer:
class YourViewController: UIViewController { // 全局强引用,避免被释放 var audioPlayer: AVPlayer? // ... 其他原有代码 func playAudio(fromData audioData: Data, url: URL) { let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let audioFileURL = documentsDirectoryURL.appendingPathComponent(url.lastPathComponent) do { try audioData.write(to: audioFileURL) DispatchQueue.main.async { let audioPlayerItem = AVPlayerItem(url: audioFileURL) // 赋值给全局属性,强持有 self.audioPlayer = AVPlayer(playerItem: audioPlayerItem) self.audioPlayer?.play() } } catch { print("Error writing audio data to file: \(error.localizedDescription)") } } }
2. 权限配置完全错误
你配置的NSFileProviderExtension是文件扩展相关权限,和当前的音频下载、播放需求完全无关:
- 写入App的Documents目录不需要额外权限,该目录默认支持读写
- 若使用HTTP链接下载(你当前用的是HTTPS,无需配置),需在
Info.plist中添加NSAppTransportSecurity配置允许非HTTPS请求 - 如需后台播放音频,需在
Info.plist的UIBackgroundModes中添加audio项
3. 未处理网络请求失败场景
你的dataTask闭包只处理了data存在的情况,完全忽略了网络错误、响应异常的场景,可能下载早已失败但你没有察觉:
session.dataTask(with: request) { data, response, error in // 先处理错误 if let error = error { print("下载失败: \(error.localizedDescription)") return } // 校验HTTP响应状态 guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { print("响应异常: \(response?.description ?? "无响应")") return } // 校验数据存在 guard let data = data else { print("无返回数据") return } // 后续处理逻辑 DispatchQueue.main.async { self.savedFiles[url.absoluteString] = data self.playAudio(fromData: data, url: url) } }.resume()
4. 新增文件写入与播放状态校验
可以在写入文件后校验文件完整性,同时监听AVPlayer的状态,确认音频是否成功加载:
do { try audioData.write(to: audioFileURL) // 校验文件是否写入成功 if FileManager.default.fileExists(atPath: audioFileURL.path) { let fileSize = try FileManager.default.attributesOfItem(atPath: audioFileURL.path)[.size] as? Int64 print("文件写入成功,大小: \(fileSize ?? 0)字节") } DispatchQueue.main.async { let audioPlayerItem = AVPlayerItem(url: audioFileURL) self.audioPlayer = AVPlayer(playerItem: audioPlayerItem) // 监听音频加载状态 audioPlayerItem.addObserver(self, forKeyPath: "status", options: [.new], context: nil) self.audioPlayer?.play() } } catch { print("写入文件失败: \(error.localizedDescription)") } // 状态监听方法 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if let playerItem = object as? AVPlayerItem, keyPath == "status" { switch playerItem.status { case .readyToPlay: print("音频准备完成") case .failed: print("音频加载失败: \(playerItem.error?.localizedDescription ?? "未知错误")") case .unknown: print("音频状态未知") @unknown default: break } } }
内容的提问来源于stack exchange,提问作者kesape
相关产品推荐
相关产品推荐

