Как конвертировать видео (в галерее) в NSData? в Свифте

У меня просто нет «информации» в Swift imagePickerController, поэтому я не знаю, как получить URL-адрес и преобразовать его в данные для отправки в веб-службу.

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

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

person Mohamed Helmy    schedule 17.10.2015    source источник
comment
Пожалуйста, уточните ваш вопрос. В чем проблема с параметром info в вашем опубликованном коде?   -  person rmaddy    schedule 17.10.2015
comment
Круто. Опубликованный код работает в Swift, а не в swift2. Я спрашиваю, как он работает в Swift 2. В этом моем параметре функции нет информации.   -  person Mohamed Helmy    schedule 17.10.2015
comment
Каким образом опубликованный код не работает в Swift 2? Метод делегата в вашем комментарии устарел. Используйте тот, что в вашем вопросе. Просто задайте для параметра info значение [String : AnyObject].   -  person rmaddy    schedule 17.10.2015
comment
Спасибо, ошибся, работает   -  person Mohamed Helmy    schedule 17.10.2015


Ответы (3)


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

Свифт 2

Вы должны использовать if let, чтобы развернуть ваши опции. Также NSData.dataWithContentsOfMappedFile устарела iOS8. Попробуйте использовать ContentOfURL инициализатора метода NSData:

Примечание. Вам также необходимо изменить объявление 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)
        }
    }
}

как упоминал Роб, данные могут быть очень большими, но вместо копирования файла вы должны переместить файл в папку документов следующим образом:

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)
    }
}
person Leo Dabus    schedule 17.10.2015

Есть несколько вопросов:

  1. Рассмотрим эту строку:

    var videoDataURL = info[UIImagePickerControllerMediaURL] as! NSURL!
    

    Это принудительно разворачивает info[UIImagePickerControllerMediaURL] (что плохо, потому что если бы это было nil, приложение вылетало бы из строя), и это приводит его к неявно развернутому необязательному NSURL!. Это не имеет смысла. Просто выполните условную развертку (и разверните на NSURL, а не на NSURL!):

    if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL { ... }
    
  2. Следующая строка вызывает filePathURL:

    var videoFileURL = videoDataURL.filePathURL
    

    Если вам нужен URL-адрес файла, он у вас уже есть, поэтому преобразование не требуется, а вместо этого просто используйте videoDataURL. Если вам действительно нужен путь, вы должны использовать метод path:

    let videoPath = videoDataURL.path
    

    Честно говоря, Apple пытается отвлечь нас от использования строковых путей, поэтому просто используйте оригинальный videoDataURL и избегайте использования path и filePathURL.

  3. Вы используете dataWithContentsOfMappedFile:

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

    Если вы действительно хотите использовать dataWithContentsOfMappedFile, правильный синтаксис Swift:

    let video = NSData(contentsOfMappedFile: videoPath!)
    

    Но dataWithContentsOfMappedFile устарело, поэтому вместо этого вы должны использовать:

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

    Или, полностью обойдя этот videoPath, вы могли бы:

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

    Очевидно, что эти try представления должны выполняться в пределах do блока с catch блоком.

  4. Кстати, как вы увидите во всех приведенных выше примерах, по возможности следует использовать let.

--

Откровенно говоря, я бы вообще не советовал загружать его в 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)
    }
}

Если вы загружаете это в веб-службу, вы должны использовать NSURLSessionUploadTask, используя параметры файла или потока. Построение этого запроса — отдельный вопрос, но, надеюсь, вы поняли идею: с большими активами, такими как фотографии или, особенно, видео, не создавайте экземпляр NSData с активом, если вы можете избежать этого.

person Rob    schedule 17.10.2015
comment
Вы также должны объяснить, как этот ответ решает проблему в вопросе. - person rmaddy; 17.10.2015
comment
@rmaddy - я разрубал гордиев узел, но изменил ответ, чтобы описать проблемы. - person Rob; 17.10.2015
comment
Привет Роб! Есть ли способ прочитать данные только в диапазоне и загрузить на сервер. Пример: Предположим, у меня есть видео размером 1 Гб, поэтому сначала я читаю 0–50 МБ и загружаю на сервер, а затем 50–100 МБ и загружаю и так далее. - person Farooque Azam; 28.01.2019
comment
@FarooqueAzam - Поскольку вы все еще имеете дело с большими загрузками (каждая по 50 МБ), я был бы склонен создавать отдельные файлы для каждого фрагмента по 50 МБ. Например. создайте InputStream и начните читать содержимое (вероятно, чтение 10-100 КБ за раз), записывая в OutputStream, который вы создаете для каждого фрагмента размером 50 МБ. Затем вы можете выполнять загрузку на основе файлов (отлично подходит для фона URLSession) этих отдельных файлов. Но это выходит за рамки этого вопроса, поэтому, если у вас действительно есть дополнительные вопросы, создайте свой собственный вопрос на Stack Overflow. - person Rob; 28.01.2019

После выбора видео этот метод будет вызван

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)
        }
        
        }
   
}
person Ahsan    schedule 30.12.2020