如何使用 URLSession 下载图片?

How to download an image using URLSessionDownload?

我需要从服务器下载图像并将其显示在我的应用程序中。我选择使用 URLSessionDownload 协议来获取下载状态和整个过程的进度。这是我的代码:

var downloadBGTask: URLSessionDownloadTask!
var downloadBGSession: URLSession!

override func viewDidLoad() {
    super.viewDidLoad()

    self.downloadBGSession = {
        let downloadBGSessionConfig = URLSessionConfiguration.background(withIdentifier: "downloadBGSession")
        return URLSession(configuration: downloadBGSessionConfig, delegate: self, delegateQueue: OperationQueue.main)
    }()

    self.downloadBGTask = self.downloadBGSession.downloadTask(with: "http.. ETC .png")
    self.downloadBGTask.resume()
}

协议

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
    self.loadingLabel.text = "Loading (\((Float(totalBytesWritten)/Float(totalBytesExpectedToWrite)) * 100)%)"
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print("Download Completed")

    // How do I get the image I downloaded?
    // This code below doesn't even compile
    self.randomImg.image = UIImage(data: downloadTask.response!)
}

有什么建议吗?

location是数据的临时文件URL。所以你可以这样做:

extension ViewController: URLSessionDownloadDelegate {
    ...

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("Download Completed")

        let data = try! Data(contentsOf: location)
        randomImg.image = UIImage(data: data)
    }
}

或者,您也可以在使用前将其保存到缓存文件夹中(因为 location 中的文件 URL 是一个临时文件,一旦您 return):

/// Local file URL for cached image

let cachedImageFileURL = try! FileManager.default
    .url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    .appendingPathComponent("saved.png")

然后:

extension ViewController: URLSessionDownloadDelegate {
    ...

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        print("Download Completed")

        imageDownloadInProgress = false

        try! FileManager.default.copyItem(at: location, to: cachedImageFileURL)
        let data = try! Data(contentsOf: location)

        randomImg.image = UIImage(data: data)
    }

}

但问题是你为什么要做背景URLSession。如果你要这样做,你必须做其他事情。例如,您的应用程序委托必须捕获完成处理程序,以防后台请求完成时应用程序不是 运行:

var backgroundRequestCompletionHandler: (() -> Void)?

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    backgroundRequestCompletionHandler = completionHandler
}

然后你的视图控制器(如果它真的是 URLSessionDelegate),必须在后台会话请求处理完成时调用这个保存的完成处理程序:

extension ViewController: URLSessionDelegate {
    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate

        appDelegate.backgroundRequestCompletionHandler?()
    }
}

但是如果您这样做,viewDidLoad 总是启动新的下载是没有意义的(因为下载可能已经通过应用程序的先前调用启动)。因此,在您发起下载请求之前,您可能希望它:

  • 查找是否存在来自先前请求的缓存文件;
  • 检查下载是否正在进行(已将此状态保存在持久存储中或使用 URLSession 异步方法 getTasksWithCompletionHandler(_:)