iOS(swift):将相册里的资源复制到本地APP

     通过UIImagePickerController(),打开相册并选取相册内的资源,其实没什么难度。之前项目中需要将相册内的视频拿到本地APP中,并进行播放。具体思路为本地某个控制器准守UIImagePickerControllerDelegate代理方法,通过一下两个方法即可拿到当前视频的路径,尽管中间有通过264压缩,但是整体功能不受影响。

     

    func initImagePickController(){
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
            let picker = UIImagePickerController()
            picker.delegate = self
            picker.sourceType = UIImagePickerControllerSourceType.photoLibrary
            picker.videoExportPreset = AVAssetExportPresetHighestQuality
            picker.mediaTypes = [kUTTypeMovie as String]
            picker.allowsEditing = false
            self.present(picker, animated: true, completion: {
                ()-> Void in
            })
        }else{
            print("读取相册错误")
        } 
    }

// 代理方法,点击哪个视频,就会调用该方法。
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        
        let mediaType = info[UIImagePickerControllerMediaType] as! String
        if mediaType == (kUTTypeMovie as String){
            let videoURL =  info[UIImagePickerControllerMediaURL] as! NSURL
            let startTime : Int  = 0
            let urlStr = videoURL.absoluteString
            self.dismiss(animated: true, completion: nil)
            print(urlStr)
            //拿到urlStr后,即可设置播放器的url。

        }
  }

       上面的代码,在获取通过相机拍照或者其他APP保存的视频都没有问题。直到有一次,通过苹果家的“隔空投送”功能将Mac上的视频投送到iPhone相册里面,在提取该视频的时候程序崩溃了。

      通过对比正确提取视频和错误提取后的info中的信息发现,能够正确提取的info[UIImagePickerControllerMediaURL]不为空,而引起崩溃的info[UIImagePickerControllerMediaURL]为nil。打断点后,在xcode的输出栏中,通过po info 命令,打印info的详细内容后发现:

正确:
▿ 3 elements
  ▿ 0 : 2 elements
    - key : "UIImagePickerControllerReferenceURL"
    - value : assets-library://asset/asset.MP4?id=CCCE2E1E-15F3-4416-8E73-B6102ACDEC15&ext=MP4
  ▿ 1 : 2 elements
    - key : "UIImagePickerControllerMediaType"
    - value : public.movie
  ▿ 2 : 2 elements
    - key : "UIImagePickerControllerMediaURL"
    - value : file:///private/var/mobile/Containers/Data/Application/ED79D02E-3516-4B82-B0F7-EA79BCF48903/tmp/7B922446-27CB-46F9-BB7E-0C3273CE45D8.MOV

错误:
▿ 2 elements
  ▿ 0 : 2 elements
    - key : "UIImagePickerControllerReferenceURL"
    - value : assets-library://asset/asset.MOV?id=7CE3AC64-2EF7-4915-B257-3D140B9F3919&ext=MOV
  ▿ 1 : 2 elements
    - key : "UIImagePickerControllerMediaType"
    - value : public.movie

    通过正常提取视频路径的后发现,通过UIImagePickerControllerMediaURL这个key,指向的是通过压缩后放入的视频路径。而UIImagePickerControllerReferenceURL是指向源视频资源的一个ID。通过某些方法,就可以将源视频提取出来。

    那么我们可以通过UIImagePickerControllerReferenceURL这个信息把视频源提取出来,复制到本地app的一个专属临时文件夹内,然后播放该视频就可以了吧。这样还可以避免了从相册里面取视频时必须压缩的尴尬局面。

    那么修改后成了一下代码:

        
        if let referenceURL = info[UIImagePickerControllerReferenceURL] as? NSURL {

            let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [referenceURL as URL], options: nil)
            if let phAsset = fetchResult.firstObject {
                PHImageManager.default().requestAVAsset(forVideo: phAsset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in
                    DispatchQueue.main.async {
                        if let asset = asset as? AVURLAsset {
                            let videoData = NSData(contentsOf: asset.url)
                            // optionally, write the video to the temp directory
                            let videoPath = NSTemporaryDirectory() + "tmpMovie.MOV"
                            let videoURL = NSURL(fileURLWithPath: videoPath)
                            let writeResult = videoData?.write(to: videoURL as URL, atomically: true)
                            if writeResult == true{                               
                             print(videoURL)
                            // 利用该videoURL获取复制到本地APP的视频资源路径,上传、下载作为播放器的视频源
                            }else{
                                print("失败")
                            }

                        }
                    }

                })
            }
        }

    这样做其实基本实现了之前设想的功能,但是xcode却报出了警告,大体意思就是UIImagePickerControllerReferenceURL方法已经过时,请利用UIImagePickerControllerPHAsset。但是通过对比上面的正确和错误的info信息知道,根本没有这个key键啊?怎么取出。

    原来在iOS11后,苹果对于用户隐私做出了某些改变,既能保护了用户的隐私,又能方便app开发者方便调用相册资源。

   iOS11之前,利用UIImagePickerController后会弹出授权窗口,意思是要访问你的相册,需要经过你的同意。反正就是只要是你想要访问相册,无论该访问有多么的微不足道,都会弹出窗口。但是在iOS11后,创建UIImagePickerController,并且present后,不在弹出,即本博客的func initImagePickController()方法执行过程不在弹出弹窗。

     而之前之所以无法通过info[UIImagePickerControllerPHAsset]获取的资源就是因为这方面的原因。通过断点发现,执行上面的代码段:PHAsset.fetchAssets()该方法时(xcode此时也提示该方法已经过时),会弹出授权窗口。利用到了PHAsset类。经过授权以后,info[UIImagePickerControllerPHAsset]不在为nil,此时打印info这个字典的信息为:

▿ 3 elements
  ▿ 0 : 2 elements
    - key : "UIImagePickerControllerReferenceURL"
    - value : assets-library://asset/asset.MP4?id=9ABEAACE-54DD-4C2F-BCEE-DEDFA12B2BDC&ext=MP4
  ▿ 1 : 2 elements
    - key : "UIImagePickerControllerPHAsset"
    - value : <PHAsset: 0x103d823e0> 9ABEAACE-54DD-4C2F-BCEE-DEDFA12B2BDC/L0/001 mediaType=2/0, sourceType=1, (528x960), creationDate=2019-05-05 09:31:04 +0000, location=0, hidden=0, favorite=0 
  ▿ 2 : 2 elements
    - key : "UIImagePickerControllerMediaType"
    - value : public.movie

此时发现,没有了UIImagePickerControllerMediaURL该key键。有了UIImagePickerControllerPHAsset该key键,获取对应的PHAsset类就可以提取对应的资源了。

    经过上面的分析可知,我们在正确的提取相册内的资源并复制到本地APP内,在点击选中视频后,在执行UIImagePickerControllerDelegate的imagePickerController()代理方法之前,必须获取访问相册的权限后才能通过info[UIImagePickerControllerPHAsset]得到PHAsset。由此整理出一下代码:

func jumpToViewController() {
//        主动获取打开系统相册权限
        if PHPhotoLibrary.authorizationStatus() == .notDetermined{
            PHPhotoLibrary.requestAuthorization { (state) in
                if state == .authorized{    
                    self.initImagePickController()  
                } else if state == .denied || state == .restricted{
                    self.dismiss(animated: true, completion: {
                        print("拒绝访问相册")
                    })
                }
            }
        }else{          
            self.initImagePickController()
        }
        
    }
// 也可以主动调用UIImageWriteToSavedPhotosAlbum()该方法,即向相册内写东西,但是我们内部可以什么都不写,只是为了获取权限。

 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let asset = info[UIImagePickerControllerPHAsset] as? PHAsset {
            PHImageManager.default().requestAVAsset(forVideo: asset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in    
                DispatchQueue.main.async {
                    if let asset = asset as? AVURLAsset {                        
                        let videoData = NSData(contentsOf: asset.url)                        
                        // optionally, write the video to the temp directory
                        let videoPath = NSTemporaryDirectory() + "tmpMovie.MOV"
                        let videoURL = NSURL(fileURLWithPath: videoPath)
                        let writeResult = videoData?.write(to: videoURL as URL, atomically: true)
                        if writeResult == true{                            
                           print(videoURL)
                           // 利用该videoURL获取复制到本地APP的视频资源路径,上传、下载作为播放器的视频源                            
                        }else{                         
                            print("失败")
                        }
                        
                    }
                }
                
            })
        }

复制文件的时候,必须在主线程中进行,否则会报错:DispatchQueue : Cannot be called with asCopy = NO on non-main thread。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值