无法使用URLSession的下载数据任务在Swift中播放mp3

问题描述 投票:0回答:1

我在main.storyboard中使用单视图控制器创建了一个空白示例项目,这是下面的实现:

import UIKit
import AVFoundation

private enum DownloadErrors: Error {
    case invalidRequest
    case noResponse
    case noTemporaryURL
    case noCacheDirectory
    case inplayable
    case fileNotExists
    case system(error: Error)

    var localizedDescription: String {
        switch self {
        case .invalidRequest:
            return "invalid request"
        case .noResponse:
            return "no response"
        case .noTemporaryURL:
            return "no temporary URL"
        case .noCacheDirectory:
            return "no cache directory"
        case .inplayable:
            return "invalid to play"
        case .fileNotExists:
            return "file not exists"
        case let .system(error):
            return error.localizedDescription
        }
    }
}


class ViewController: UIViewController {

    // MARK: - View Life Cycle

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.test()
    }

    // MARK: - Private API

    private func test() {
        self.download(with: self.request, completion: { result in
            switch result {
            case let .failure(error):
                print("failure: \(error.localizedDescription)")
                return
            case let .success(url):
                self.play(atURL: url)
            }
        })
    }

    private let request: URLRequest? = {
        var components = URLComponents()
        components.scheme = "https"
        components.host = "islex.arnastofnun.is"
        components.path = "/islex-files/audio/10/1323741.mp3"
        guard let url = components.url else {
            return nil
        }
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.setValue("audio/mpeg", forHTTPHeaderField: "Content-Type")
        request.setValue(
            "attachment; filename=\"1323741.mp3\"",
            forHTTPHeaderField: "Content-Disposition"
        )
        print(request.url?.absoluteString ?? "invalid URL")
        return request
    }()

    private typealias Callback = (Result<URL, DownloadErrors>) -> Void

    private func download(with nilableRequest: URLRequest?, completion: @escaping Callback) {
        guard let request = nilableRequest else {
            completion(.failure(.invalidRequest))
            return
        }
        let task = URLSession.shared.downloadTask(with: request) { (rawTemporaryFileURL, rawResponse, rawError) in
            if let error = rawError {
                completion(.failure(.system(error: error)))
                return
            }
            guard let httpStatusCode = (rawResponse as? HTTPURLResponse)?.statusCode else {
                completion(.failure(.noResponse))
                return
            }
            print("http status code: \(httpStatusCode)")
            guard let sourceFileURL = rawTemporaryFileURL else {
                completion(.failure(.noTemporaryURL))
                return
            }
            guard let cache = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
                completion(.failure(.noCacheDirectory))
                return
            }
            let targetFileURL = cache.appendingPathComponent("audio_10_1323741.mp3")
            if !FileManager.default.fileExists(atPath: targetFileURL.path) {
                do {
                    try FileManager.default.moveItem(at: sourceFileURL, to: targetFileURL)
                } catch let error {
                    completion(.failure(.system(error: error)))
                }
            }
            DispatchQueue.main.async {
                completion(.success(targetFileURL))
            }
        }
        task.resume()
    }

    private func play(atURL url: URL) {
        do {
            guard FileManager.default.fileExists(atPath: url.path) else {
                print("play: \(DownloadErrors.fileNotExists)")
                return
            }
            let player = try AVAudioPlayer(contentsOf: url)
            player.volume = 1.0
            player.prepareToPlay()
            player.play()
            print("play: finished")
        } catch let error {
            print("play error: \(error.localizedDescription)")
        }
    }
}

我做错了什么?我有一个成功的响应,尝试使用url创建音频播放器时没有出现错误,但是我的播放器没有播放任何内容。我尝试从链接下载并立即播放文件:

https://islex.arnastofnun.is/islex-files/audio/10/1323741.mp3

我的Xcode登录控制台:

https://islex.arnastofnun.is/islex-files/audio/10/1323741.mp3
http status code: 200
play: finished
swift mp3 urlsession
1个回答
0
投票

由于您位于完成块内,因此您很可能需要将调用封装在DispatchQueue块内,以便在主线程上显式运行它。简而言之,您可以这样做:

DispatchQueue.main.async {
   self.play(atURL: url)
}
© www.soinside.com 2019 - 2024. All rights reserved.