【问题标题】:How to save a remote image with Swift?如何使用 Swift 保存远程图像?
【发布时间】:2014-10-09 18:44:56
【问题描述】:

我正在尝试使用 Swift 显示和保存图像。第一次点击时,它在 imageview 上显示远程图像,第二次点击它显示空白 imageview 而不是它应该是第一次点击时保存的本地图像。

    var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
    var imagePath = paths.stringByAppendingPathComponent("images/\(id)/logo.jpg" )
    var checkImage = NSFileManager.defaultManager()

    if (checkImage.fileExistsAtPath(imagePath)) {
        let getImage = UIImage(contentsOfFile: imagePath)
        self.image?.image = getImage
    } else {
        dispatch_async(dispatch_get_main_queue()) {
            let getImage =  UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage)))
            UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true)
            self.image?.image = getImage
        }
    }

编辑:这个对我有用。

var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
var dirPath = paths.stringByAppendingPathComponent("images/\(id)" )
var imagePath = paths.stringByAppendingPathComponent("images/\(id)/logo.jpg" )
var checkImage = NSFileManager.defaultManager()

if (checkImage.fileExistsAtPath(imagePath)) {
    let getImage = UIImage(contentsOfFile: imagePath)
    self.image?.image = getImage
} else {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
        checkImage.createDirectoryAtPath(dirPath, withIntermediateDirectories: true, attributes: nil, error: nil)
        let getImage =  UIImage(data: NSData(contentsOfURL: NSURL(string: remoteImage)))
        UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true)

        dispatch_async(dispatch_get_main_queue()) {
            self.image?.image = getImage
            return
        }
    }
}

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    要回答您的主要问题,您调用了错误的 UIImage 初始化程序。您应该在 swift 2 中调用 UIImage(contentsOfFile: imagePath),在 swift 3 中调用 UIImage(contentsOf: imagePath)

    此外,您似乎正在尝试使用 dispatch_async(或 swift 3 中的 DispatchQueue)在后台进行远程获取,但您将其传递给主队列,因此您实际上是在阻塞主/ UI线程。您应该将其分派到后台队列之一,然后在您实际在 UI 中设置图像时将其分派回主队列:

    斯威夫特 3:

    DispatchQueue.global(qos: DispatchQoS.background.qosClass).async {
        do {
            let data = try Data(contentsOf: URL(string: self.remoteImage)!)
            let getImage = UIImage(data: data)
            try UIImageJPEGRepresentation(getImage!, 100)?.write(to: imagePath)
            DispatchQueue.main.async {
                self.image?.image = getImage
                return
            }
        }
        catch {
                return
        }
    }
    

    斯威夫特 2:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
        let getImage =  UIImage(data: NSData(contentsOfURL: NSURL(string: self.remoteImage)))
        UIImageJPEGRepresentation(getImage, 100).writeToFile(imagePath, atomically: true)
    
        dispatch_async(dispatch_get_main_queue()) {
            self.image?.image = getImage
            return
        }
    }
    

    @Rob's answer re: 获取远程图像并保存它确实是最好的方法。

    【讨论】:

    • 谢谢,我把它改成了 UIImage(contentsOfFile: imagePath)。但是当我将调度更改为 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) 它甚至没有显示远程图像。
    • 谢谢,我认为调度现在效果更好了。但我仍然无法在本地加载图像。我认为 "var imagePath = paths.stringByAppendingPathComponent("images/(id)/logo.jpg" )" 是问题所在,因为 "if block" 根本没有出现。
    • 您是否设置了断点并查看了imagePath 以确保它有意义? (你也可以println(imagePath)
    • 当我将路径更改为“(id)-logo.jpg”时,它起作用了。问题应该是子目录。
    • @bilmiyore 只需调用NSFileManager 方法createDirectoryAtPath
    【解决方案2】:

    NSData(contentsOfURL:)(现在称为Data(contentsOf:))分派到主队列的代码。如果您打算使用该同步方法来请求远程图像,您应该在后台队列中执行此操作。

    此外,您正在获取NSData,将其转换为UIImage,然后使用UIImageJPEGRepresentation 将其转换回NSData。不要通过UIImageJPEGRepresentation 往返,因为您将更改原始有效负载并更改资产的大小。只需确认数据包含图像,然后写入原始NSData

    因此,在 Swift 3 中,您可能想要执行以下操作:

    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)
        }
    }
    

    更好的是,您应该使用NSURLSession,因为您可以更好地诊断问题,它是可取消的等等(并且不要使用已弃用的NSURLConnection。)我还会检查回复的statusCode。例如:

    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()
    
    }
    

    请注意,仅当您尚未创建文件夹时,才需要即时创建文件夹。就个人而言,当我构建原始路径时,我会在那里而不是在完成处理程序中创建文件夹,但您可以按照您想要的任何方式执行此操作。只需在写入文件之前确保该文件夹存在即可。

    无论如何,希望这能说明要点,即您应该保存原始资产并且应该在后台执行此操作。

    对于 Swift 2 版本,请参阅 previous revision of this answer

    【讨论】:

    • 我刚试过,但无法加载任何远程或本地图像。
    • @bilmiyore 仅供参考,我修改了我的答案以改进错误处理以诊断出了什么问题,如果有的话。在本地图像加载方面,我没有触及。
    • 谢谢@Rob,这也有效。只有一个问题是我无法使其与子目录一起使用。 “(id)-logo.jpg” 路径有效,但“(id)\logo.jpg” 无效。
    • @bilmiyore 您不能只保存到不存在的子目录。如果该子目录不存在,您必须创建该子目录(使用NSFileManager)。顺便说一句,请参阅我的最后一个示例,其中我向您展示了如何使用 writeToFile 以向您提供有意义的错误消息。
    猜你喜欢
    • 1970-01-01
    • 2011-10-05
    • 2018-03-27
    • 1970-01-01
    • 2011-06-10
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 2018-06-05
    相关资源
    最近更新 更多