Доступ к файлам изображений на фотопленке (iOS 12.1.4, Swift 4.2, Xcode 10.1)

#swift #xcode #url #camera

#swift #xcode #url #камера

Вопрос:

Я пытаюсь вычислить хэш файла для JPEG файлов (например MD5 ) после съемки встроенной камерой. Я могу создать дайджест данных изображения, но это не будет совпадать с фактическим хэшем файла. Глядя на модуль Photo и его PHAsset класс, я не вижу способа получить фактический путь к файлу (URL), который я мог бы затем передать MD5Digest . Возможно, можно захватить файл URL сразу после сохранения изображения на фотопленке, реализовав функцию прослушивания завершения. Кто-нибудь пробовал это раньше?

Обновлен код с помощью completionHandler, но мне все еще нужно найти URL.

  @IBOutlet weak var imageHash: UILabel!

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

    let orgPic = info[UIImagePickerController.InfoKey.originalImage] as? UIImage

    UIImageWriteToSavedPhotosAlbum(orgPic!, self, #selector(imageSaved(_:didFinishSavingWithError:contextInfo:)), nil)

}

@objc func imageSaved(_ img: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
        if let error = error {
            // we got back an error!
            let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            present(ac, animated: true)
        } else {
            let ac = UIAlertController(title: "Saved!", message: "(img.ciImage?.url)", preferredStyle: .alert)
            ac.addAction(UIAlertAction(title: "OK", style: .default))
            present(ac, animated: true)
        }
 }

// the img.ciImage.url is "nil" unfortunately
  

Комментарии:

1. Привет, Ико, было бы здорово, если бы ты могла добавить какой-нибудь код, который помог бы понять проблему.

2. Хэш MD5 над данными изображения отличается от хэша файла над JPEG на фотопленке. UIImageWriteToSavedPhotosAlbum(), вероятно, удаляет некоторые данные, а затем применяет сжатие JPEG перед сохранением. Единственный способ вычислить истинный хэш файла — это найти URL-адрес файла и прочитать его примерно так: «пусть data = try Data(contentsOf: someFileURL)», затем запустить хэш, как в «пусть digest = data.md5». Мой вопрос в том, могу ли я найти URL-адрес файла, предоставив обработчик завершения в UIImageWriteToSavedPhotosAlbum (будет третьим аргументом)? Большое спасибо -Iko

Ответ №1:

Итак, ваш главный девиз — сравнить MD5 из 2 файлов изображений. Итак, вот что я попробовал, и, возможно, это поможет вам

; TLDR — файл, сохраненный в библиотеке, и фактический файл у меня не совпадали

  public func saveImgToLocal(_ img: UIImage,
                           onComplete: @escaping () -> Void) {

    var blockPlaceholder: PHObjectPlaceholder?
    var changeRequest: PHAssetChangeRequest?

    PHPhotoLibrary.shared().performChanges({
        changeRequest =  PHAssetChangeRequest.creationRequestForAsset(from: img)
        blockPlaceholder = changeRequest?.placeholderForCreatedAsset
    }, completionHandler: { (saved, error) in
        //_saved_ is of type bool, you can use it to check whether image is saved
        //Here we would fetch the asset using our saved placeholder
        let fetchOptions = PHFetchOptions()
        let fetchResult:PHFetchResult = PHAsset.fetchAssets(withLocalIdentifiers: [blockPlaceholder!.localIdentifier], options: fetchOptions)
        if let asset = fetchResult.firstObject {
            //Method to convert asset to Actual image
            self.getAsset(asset: asset)
        }
        onComplete()

    })
}
  

Вот как я сохранил изображение в Local

Вот как я получил изображение от Local

 func getAsset(asset: PHAsset)  {
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    var resultImage = UIImage()
    option.isSynchronous = true
    manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option, resultHandler: {(result, info)->Void in
        resultImage = result!
        let url = info!["PHImageFileURLKey"] as! NSURL
        //Image created using the file URL.
        let image = UIImage.init(contentsOfFile: url.path!)
//I even compared the resultImage instead of the image from the file
        if UIImagePNGRepresentation(image!) == UIImagePNGRepresentation(self.image!) {
            print("Same image")
        }
    })

}
  

Так что, если вы просто хотите вернуть сохраненное изображение, это можно сделать, но я не уверен, сохранено ли изображение в том виде, в каком оно есть в библиотеке фотографий.
HTH 🙂

Комментарии:

1. Идеальный. Работает как по волшебству. Спасибо, что положили конец 2 дням страданий 😉

2. Спасибо 🙂 Можете ли вы принять ответ, если это поможет? Мне нужны очки:-p

Ответ №2:

Хотя опубликованный Аджинкьей Шармой код работал, я заметил, что производительность сохранения изображений на фотопленке была значительно ниже при использовании creationRequestForAsset по сравнению с UIImageWriteToSavedPhotosAlbum. Поэтому я выбрал гибридное решение. Я сохранил, используя старую функцию, затем использовал PHAsset framework для извлечения новейшего файла изображения и получения его URL. Проблема решена.

 public func saveImgToLocal(_ img: UIImage) {
    // significantly faster than creationRequestForAsset
    UIImageWriteToSavedPhotosAlbum(img, self, #selector(imageSaved(_:didFinishSavingWithError:contextInfo:)), nil)
}

@objc func imageSaved(_ img: UIImage, didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer) {
    if let error = error {
        // we got back an error!
        let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "OK", style: .default))
        present(ac, animated: true)
    } else {
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        fetchOptions.fetchLimit = 1
        let fetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)
        if let asset = fetchResult.firstObject
        {
            self.imageHash.text = getAsset(asset: asset)
        }
    }
}

func getAsset(asset: PHAsset) -> String {
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    var hashString = "" as String
    option.isSynchronous = true
    manager.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: option, resultHandler: {(result, info) -> Void in
        let url = info!["PHImageFileURLKey"] as! NSURL
        do {
            let data = try Data(contentsOf: url as URL)
            hashString = data.md5.rawValue
        }
        catch {
            hashString = "Error computing hash"
        }
    })
    return hashString
}