代码之家  ›  专栏  ›  技术社区  ›  K.Wu

有权在swift中读取视频文件而不是图像文件?

  •  1
  • K.Wu  · 技术社区  · 7 年前

    我写了一个图像选取器( 不是 UIImagePickerController )在swift中,按“完成”键后,我返回选定相集(图像或视频或两者)的url,然后将它们上载到firebase存储器。

    图像的URL如下所示: file:///var/mobile/Media/DCIM/101APPLE/IMG_1239.PNG

    视频的URL如下所示: file:///var/mobile/Media/DCIM/101APPLE/IMG_1227.MOV

    然后我显示相位集。播放视频资源时没有问题:

    let player = AVPlayer(url: url)
    let playerLayer = AVPlayerLayer(player: player)
    playerLayer.frame = view.frame
    playerLayer.videoGravity = .resizeAspectFill
    view.layer.addSublayer(playerLayer)
    player.play()
    

    但是,当我尝试使用图像的url显示图像时,会出现“无权读取文件”错误:

    do {
        let data = try Data(contentsOf: url)
        imageView.image = UIImage(data: data)
    } catch {
        print(error)
    }
    

    我有权限访问用户的照片库,这就是为什么图像选取器工作正常的原因。我只是不能用他们的网址读取图像文件。

    我在想,也许我应该将图像资源复制到一个临时文件中并从中读取它,但是为什么视频资源是可读的而不是图像资源是没有意义的。

    ——————————————————————————————————————————————更新-

    这就是我如何获取相集的url

    extension PHAsset {
        func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)) {
            if self.mediaType == .image {
                let options = PHContentEditingInputRequestOptions()
                options.canHandleAdjustmentData = { (adjustmeta: PHAdjustmentData) -> Bool in
                    return true
                }
                self.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) in
                    completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
                })
            } else if self.mediaType == .video {
                let options = PHVideoRequestOptions()
                options.version = .original
                PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) in
                    if let urlAsset = asset as? AVURLAsset {
                        let localVideoUrl: URL = urlAsset.url as URL
                        completionHandler(localVideoUrl)
                    } else {
                        completionHandler(nil)
                    }
                })
            }
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  4
  •   staticVoidMan    7 年前

    ios中的应用程序以沙盒模式运行,这样可以防止它访问自己范围之外的文件。
    URL,例如:

    文件:///var/mobile/media/dcim/101apple/…

    不在应用程序的作用域内,因此无法通过 Data(contentsOf:)

    AVPlayer 但是,可以从外面进入 url 因为它有特殊的权限,所以你可以使用 网址 .

    网址 ,我们会用这个 网址 在内部 PHAssets.fetchAssets(withALAssetURLs:options:) 为了找回 PHAsset 为了加载它,但在ios 11中该方法已被弃用。

    作为另一种选择, PHAssets.fetchAssets(withLocalIdentifiers:options:) 是可用的,但是为了使用它,您需要提供一个 localIdentifier .
    为了这个,而不是保存 网址 ,您应该保存 相位集 S 定位识别器 S.


    总结:

    1. 把你的 相位集 S
    2. 保存 asset.localIdentifier 而不是他们的 网址 S
    3. 必要时,返回 相位集 使用 [1] :

      PHAssets.fetchAssets(withLocalIdentifiers:options:)
      
    4. 处理 相位集 据它所说 mediaType

      • 为了形象 [2] :

        PHImageManager().requestImageData(for:options:resultHandler:)
        
      • 用于视频 [3] :

        PHImageManager().requestAVAsset(forVideo:options:resultHandler:)
        

    一般示例:

    警告: fatalError() 如果您的设置有问题,则用于故意终止应用程序。
    如果它崩溃了,检查你的 Info.plist 对于 NSPhotoLibraryUsageDescription 和/或照片库的权限状态,通过 PHPhotoLibrary.requestAuthorization(_:)

    1. 获取最新资产的演示功能(图像/视频;由您决定)

      func getLatestAsset(for type: PHAssetMediaType) {
          let fetchOptions = PHFetchOptions()
          fetchOptions.sortDescriptors = [NSSortDescriptor(key:"creationDate",
                                                           ascending: false)]
          fetchOptions.fetchLimit = 2
      
          let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: type,
                                                               options: fetchOptions)
      
          guard let asset = fetchResult.firstObject else { fatalError("Unable to fetch photos") }
      
          //save `localIdentifier` for later use (app relaunch or whatever)
          let assetIdentifier = asset.localIdentifier
      
          print(assetIdentifier)
      
          //Just a demo that you can get the same asset based on a local identifier
          loadAsset(identifier: assetIdentifier)
      }
      
    2. 基于 定位识别器

      func loadAsset(identifier: String) {
          guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier],
                                                options: nil).firstObject
              else {
                  fatalError("No Asset found with identifier: \(identifier)")
          }
      
          print(asset.localIdentifier)
      
          //Do something with the asset now
          if asset.mediaType == .image {
              loadImage(asset: asset)
          }
          else if asset.mediaType == .video {
              loadVideo(asset: asset)
          }
      }
      
    3. 从加载图像 相位集

      func loadImage(asset: PHAsset) {
          PHImageManager().requestImageData(for: asset, options: nil) { (data, string, orientation, userInfo) in
              guard let data = data else { fatalError("Image Data is nil") }
      
              DispatchQueue.main.async {
                  self.imageView.image = UIImage(data: data)
              }
          }
      }
      
    4. 从加载视频 相位集

      func loadVideo(asset: PHAsset) {
          PHImageManager().requestAVAsset(forVideo: asset, options: nil) { (avAsset, audioMix, userInfo) in
              guard let avAsset = avAsset else { fatalError("AVAsset is nil") }
      
              let playerItem = AVPlayerItem(asset: avAsset)
              let player = AVPlayer(playerItem: playerItem)
              let playerLayer = AVPlayerLayer(player: player)
              playerLayer.frame = self.view.frame
              playerLayer.videoGravity = .resizeAspectFill
      
              DispatchQueue.main.async {
                  self.view.layer.addSublayer(playerLayer)
                  player.play()
              }
          }
      }
      

    附言:你将不再需要 getURL(completionHandler:) 因为你的整个逻辑将基于 定位识别器 而不是 网址 我会说那更好。
    Url 只是,有点,变化无常,所以让我们至少在这里避开他们。