如何使用Swift保存远程图像?

bil*_*ore 16 ios swift

我正在尝试使用Swift显示和保存图像.在第一次点击时,它在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
        }
    }
Run Code Online (Sandbox Code Playgroud)

编辑:这个对我有用.

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mik*_*e S 20

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

此外,它看起来像你正在试图做的与背景的远程读取dispatch_async(或DispatchQueue在SWIFT 3),但你传递给它的主队列,那么你实际上阻止与主/ 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
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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
    }
}
Run Code Online (Sandbox Code Playgroud)

@Rob的回答:获取远程图像并保存它确实是最好的方法.


Rob*_*Rob 10

您的代码NSData(contentsOfURL:)(现在称为Data(contentsOf:))发送到主队列.如果您要使用该同步方法来请求远程映像,则应在后台队列上执行此操作.

此外,您正在将其NSData转换为a UIImage,然后将其转换回NSData使用状态UIImageJPEGRepresentation.尽管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)
    }
}
Run Code Online (Sandbox Code Playgroud)

更好的是,你应该使用,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()

}
Run Code Online (Sandbox Code Playgroud)

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

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

对于Swift 2再现,请参阅此答案的先前修订版.