如何在Swift中将图库中的视频转换为NSData?

5

我在Swift imagePickerController中没有“info”信息,因此不知道如何获取URL并将其转换为数据以发送到Web服务。

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {

  var videoDataURL = info[UIImagePickerControllerMediaURL] as! NSURL!
  var videoFileURL = videoDataURL.filePathURL
  var video = NSData.dataWithContentsOfMappedFile("\(videoDataURL)")
}

请说明您的问题。您发布的代码中,“info”参数有什么问题? - rmaddy
这是翻译后的文本: 这个发布的代码可以在Swift中工作,但不适用于Swift2。我想知道它如何在Swift 2中工作,这是我的函数参数,其中没有信息:"func imagePickerController(picker:UIImagePickerController,didFinishPickingImage image:UIImage,editingInfo:[String:AnyObject]?)" - Mohamed Helmy
发布的代码在Swift 2中有什么问题?您评论中的委托方法已被弃用,请使用问题中的方法。只需将“info”参数修复为“[String:AnyObject]”。 - rmaddy
谢谢,我错了,它起作用了。 - Mohamed Helmy
4个回答

8

Xcode 10 • Swift 4.2或更高版本

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    if let url = info[.mediaURL] as? URL {
        do {
            try FileManager.default.moveItem(at: url, to: documentsDirectoryURL.appendingPathComponent("videoName.mov"))
            print("movie saved")
        } catch {
            print(error)
        }
    }
}

Xcode 8.3 • Swift 3.1

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
    let documentsDirectoryURL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    if let fileURL =  info[UIImagePickerControllerMediaURL] as? URL {
        do {
            try  FileManager.default.moveItem(at: fileURL, to: documentsDirectoryURL.appendingPathComponent("videoName.mov")
            print("movie saved")
        } catch {
            print(error)
        }
    }
}

Swift 2

使用if let来解包你的可选项,同时NSData.dataWithContentsOfMappedFile已在iOS8中弃用。试试使用NSData方法initializer contentsOfURL:

注意:你还需要将didFinishPickingMediaWithInfo声明从[NSObject : AnyObject]更改为[String : AnyObject]

func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
    if let fileURL = info[UIImagePickerControllerMediaURL] as? NSURL {
        if let videoData = NSData(contentsOfURL: fileURL) {
            print(videoData.length)
        }
    }
}

正如Rob所提到的,数据可能非常大,但是你不应该复制文件,而应该将文件移动到文档文件夹中,方法如下:

let documentsDirectoryURL =  try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
if let fileURL =  info[UIImagePickerControllerMediaURL] as? NSURL {
    do {
        try  NSFileManagerdefaultManager().moveItemAtURL(fileURL, toURL: documentsDirectoryURL.URLByAppendingPathComponent("videoName").URLByAppendingPathExtension("mov"))
        print("movie saved")
    } catch {
        print(error)
    }
}

7

有几个问题:

  1. Consider this line:

    var videoDataURL = info[UIImagePickerControllerMediaURL] as! NSURL!
    

    This does a forced unwrapping of info[UIImagePickerControllerMediaURL] (which is bad, because if it was nil, the app would crash) and that casts it as an implicitly unwrapped optional NSURL!. That doesn't make sense. Just do a conditional unwrapping (and unwrap to a NSURL, not a NSURL!):

    if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL { ... }
    
  2. The next line calls filePathURL:

    var videoFileURL = videoDataURL.filePathURL
    

    If you wanted a file URL, you already have one, so no conversion is needed, but instead just use videoDataURL. If you really wanted a path, you'd use path method:

    let videoPath = videoDataURL.path
    

    Frankly, Apple is trying to shift us away from using string paths, so just use the original videoDataURL and avoid the use of both path and filePathURL.

  3. You are using dataWithContentsOfMappedFile:

    var video = NSData.dataWithContentsOfMappedFile("\(videoDataURL)")
    

    If you really wanted to use dataWithContentsOfMappedFile, the proper Swift syntax is:

    let video = NSData(contentsOfMappedFile: videoPath!)
    

    But dataWithContentsOfMappedFile deprecated, so you should instead use:

    let video = try NSData(contentsOfFile: videoPath!, options: .DataReadingMappedIfSafe)
    

    Or, bypassing that videoPath altogether, you could:

    let video3 = try NSData(contentsOfURL: videoDataURL, options: .DataReadingMappedIfSafe)
    

    Obviously, those try renditions should be done within a do block with a catch block.

  4. By the way, as you'll see in all of my above examples, one should use let where possible.

--

坦率地说,我建议根本不要将其加载到 NSData 中。只需使用 NSFileManager 复制即可,这是更有效的内存使用方式。如果视频很长,则可能相当大,因此您应避免在任何给定时间点将其全部加载到内存中。

因此,您可以:

if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL {
    do {
        // build your destination URL however you want
        //
        // let tempFolder = NSURL(fileURLWithPath: NSTemporaryDirectory())
        // let destinationURL = tempFolder.URLByAppendingPathComponent("test.mov")

        // or 

        let documents = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
        let destinationURL = documents.URLByAppendingPathComponent("test.mov")

        // but just copy from the video URL to the destination URL

        try NSFileManager.defaultManager().copyItemAtURL(videoDataURL, toURL: destinationURL)
    } catch {
        print(error)
    }
}

如果您要将此上传到Web服务,则可以使用NSURLSessionUploadTask,并使用文件或流选项。如何构建此请求是一个单独的问题,但希望您能够理解:对于像照片或尤其是视频这样的大型资产,如果可能的话,请勿使用NSData实例化该资产。


你还应该解释一下这个答案是如何解决问题的。 - rmaddy
@rmaddy - 我本来想简化问题,但我修改了答案以描述这些问题。 - Rob
嗨,罗布!我能否只读取范围内的数据并上传到服务器?例如:假设我有一个1 GB的视频,那么我首先读取0-50 MB并上传到服务器,然后读取50-100 MB并上传,以此类推。 - Farooque Azam
@FarooqueAzam - 因为你仍在处理大型上传(每个50MB),我倾向于为每个50MB块创建单独的文件。例如,创建InputStream并开始读取内容(可能每次读取10-100kb),为每个50MB块创建一个OutputStream进行写入。然后,您可以对这些单独的文件进行基于文件的上传(非常适合后台URLSession)。但这超出了本问题的范围,因此如果您确实有进一步的问题,请在Stack Overflow上创建自己的问题。 - Rob

1
选择视频后,将调用此方法。
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true, completion: nil)
    print("in here")
    if let pickedVideo = info[UIImagePickerController.InfoKey(rawValue: UIImagePickerController.InfoKey.mediaURL.rawValue)] as? URL {
            print(pickedVideo)
        do {
            print("Converted")
           // let VideoData = try Data(contentsOf: pickedVideo)
            uploadVideo(VideoData: try Data(contentsOf: pickedVideo), URL: pickedVideo)
            
        } catch let error {
            print(error.localizedDescription)
        }
        
        }
   
}

0

寻找简单的解决方案:

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

    self.dismiss(animated: true, completion: nil)
    // Using just the information key value
    if let url = info[.mediaURL] as? URL {
        // Do something with the URL
        print("Media URL : ",url)
        do {
            let videoData = try Data(contentsOf: url)
        }
        catch let error {
            print(error)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接