简体   繁体   中英

How to convert video (in gallery) to NSData? in Swift

I just haven't "info" in Swift imagePickerController so I don't know how get url and convert it to data to send to web-service.

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

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

Xcode 10 • Swift 4.2 or later

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

You should use if let to unwrap your optionals. Also NSData.dataWithContentsOfMappedFile was deprecated iOS8. Try using NSData method initializer contentsOfURL:

Note: You need also to change the didFinishPickingMediaWithInfo declaration from [NSObject : AnyObject] to [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)
        }
    }
}

as mentioned by Rob the data can be really large but instead of copying the file you should move the file to the documents folder as follow:

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)
    }
}

There are a couple of issues:

  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.

--

Quite frankly, I would advise against loading it into a NSData at all. Just copy it with NSFileManager , which is a more efficient use of memory. If the video is long, it could be quite large, and you should avoid loading the whole thing into memory at any given point in time.

So you could:

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)
    }
}

If you're uploading this to a web service, you'd then use a NSURLSessionUploadTask , using file or stream options. The construction of this request is a separate question, but hopefully you get the idea: With large assets like photos or, especially, videos, don't instantiate a NSData with the asset if you can possibly avoid it.

After picking video this method will get called

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)
        }
        
        }
   
}

Find the Simple Solution :

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)
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM