How to save a remote image with Swift?

前端 未结 2 1983
自闭症患者
自闭症患者 2021-01-31 20:52

I\'m trying to display and save images with Swift. On first hit, it shows the remote image on imageview, on second hit it shows blank imageview instead of it should be local ima

2条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-31 21:29

    Your code that dispatches NSData(contentsOfURL:) (now known as Data(contentsOf:)) to the main queue. If you're going to use that synchronous method to request remote image, you should do this on a background queue.

    Also, you are taking the NSData, converting it to a UIImage, and then converting it back to a NSData using UIImageJPEGRepresentation. Don't round-trip it though UIImageJPEGRepresentation as you will alter the original payload and will change the size of the asset. Just just confirm that the data contained an image, but then write that original NSData

    Thus, in Swift 3, you probably want to do something like:

    DispatchQueue.global().async {
        do {
            let data = try Data(contentsOf: URL(string: urlString)!)
            if let image = UIImage(data: data) {
                try data.write(to: fileURL)
                DispatchQueue.main.async {
                    self.imageView?.image = image
                }
            }
        } catch {
            print(error)
        }
    }
    

    Even better, you should use NSURLSession because you can better diagnose problems, it's cancelable, etc. (And don't use the deprecated NSURLConnection.) I'd also check the statusCode of the response. For example:

    func requestImage(_ url: URL, fileURL: URL) {
        let task = URLSession.shared.dataTask(with: url) { data, response, error in
            // check for fundamental network issues (e.g. no internet, etc.)
    
            guard let data = data, error == nil else {
                print("dataTask error: \(error?.localizedDescription ?? "Unknown error")")
                return
            }
    
            // make sure web server returned 200 status code (and not 404 for bad URL or whatever)
    
            guard let httpResponse = response as? HTTPURLResponse, 200 ..< 300 ~= httpResponse.statusCode else {
                print("Error; Text of response = \(String(data: data, encoding: .utf8) ?? "(Cannot display)")")
                return
            }
    
            // save image and update UI
    
            if let image = UIImage(data: data) {
                do {
                    // add directory if it doesn't exist
    
                    let directory = fileURL.deletingLastPathComponent()
                    try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
    
                    // save file
    
                    try data.write(to: fileURL, options: .atomic)
                } catch let fileError {
                    print(fileError)
                }
    
                DispatchQueue.main.async {
                    print("image = \(image)")
                    self.imageView?.image = image
                }
            }
        }
        task.resume()
    
    }
    

    Note, the just-in-time creation of the folder is only necessary if you haven't created it already. Personally, when I build the original path, I'd create the folder there rather than in the completion handler, but you can do this any way you want. Just make sure the folder exists before you write the file.

    Regardless, hopefully this illustrates the main points, namely that you should save the original asset and that you should do this in the background.

    For Swift 2 renditions, see previous revision of this answer.

提交回复
热议问题