Как сохранить удаленное изображение с помощью Swift?
Я пытаюсь отображать и сохранять изображения с помощью Swift. При первом попадании он показывает удаленное изображение в режиме просмотра изображений, при втором ударе он показывает пустое изображение, а не локальное изображение, которое сохраняется при первом попадании.
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
}
}
}
Ответы
Ответ 1
Чтобы ответить на ваш основной вопрос, вы вызываете неправильный инициализатор UIImage
. Вы должны называть UIImage(contentsOfFile: imagePath)
в swift 2 и UIImage(contentsOf: imagePath)
в swift 3.
Кроме того, похоже, что вы пытаетесь выполнить удаленную выборку в фоновом режиме с помощью dispatch_async
(или DispatchQueue
в swift 3), но вы передаете ей основную очередь, так что вы фактически блокируете основной/пользовательский поток с этим. Вы должны отправить его в одну из фоновых очередей, а затем отправить обратно в основную очередь, когда вы действительно установите изображение в своем интерфейсе:
Swift 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
}
}
Swift 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 answer re: извлечение удаленного изображения и сохранение его на самом деле - лучший способ сделать это.
Ответ 2
Ваш код, который отправляет NSData(contentsOfURL:)
(теперь известный как Data(contentsOf:)
) в основную очередь. Если вы собираетесь использовать этот синхронный метод для запроса удаленного изображения, вы должны сделать это в фоновом режиме.
Кроме того, вы берете NSData
, преобразовывая его в 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)
}
}
Еще лучше, вы должны использовать 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 см. предыдущую версию этого ответа.