I have an app that successfully records and uploads a file to my PHP server, but unfortunately the file either uploads as 0 bytes or won't play. I received guidance that I needed to use
AVAssetExportSession
to convert the file to mp4
to get it to work but am having trouble incorporating this into my code correctly. Any help is greatly appreciated.//Image Picker Code
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { print("Got a video") if let pickedVideo:URL = (info[UIImagePickerControllerMediaURL] as? URL) { / let selectorToCall = #selector(CameraVideoViewController.videoWasSavedSuccessfully(_:didFinishSavingWithError:context:)) UISaveVideoAtPathToSavedPhotosAlbum(pickedVideo.relativePath, self, selectorToCall, nil) imageSelected = true uuid = UUID().uuidString if imageSelected == true { saveFileName = "video-\(uuid).mp4" } / let videoData = try? Data(contentsOf: pickedVideo) let paths = NSSearchPathForDirectoriesInDomains( FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true) let documentsDirectory: URL = URL(fileURLWithPath: paths[0]) let dataPath = documentsDirectory.appendingPathComponent(saveFileName) try! videoData?.write(to: dataPath, options: []) print("Saved to " + dataPath.absoluteString) imagePicker.dismiss(animated: true, completion: { / self.encodeVideo(dataPath as AnyObject) / }) } }
// Convert to mp4 code
func encodeVideo(_ videoData: AnyObject) { let avAsset = AVURLAsset(url: videoData as! URL, options: nil) let startDate = Foundation.Date() / let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough) / / let documentsDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] let myDocumentPath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("temp.mp4").absoluteString let url = URL(fileURLWithPath: myDocumentPath) let documentsDirectory2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL let filePath = documentsDirectory2.appendingPathComponent("rendered-Video.mp4") deleteFile(filePath) / if FileManager.default.fileExists(atPath: myDocumentPath) { do { try FileManager.default.removeItem(atPath: myDocumentPath) } catch let error { print(error) } } exportSession!.outputURL = filePath exportSession!.outputFileType = AVFileType.mp4 exportSession!.shouldOptimizeForNetworkUse = true let start = CMTimeMakeWithSeconds(0.0, 0) let range = CMTimeRangeMake(start, avAsset.duration) exportSession?.timeRange = range exportSession!.exportAsynchronously(completionHandler: {() -> Void in switch exportSession!.status { case .failed: print("%@",exportSession?.error! as Any) case .cancelled: print("Export canceled") case .completed: / let endDate = Foundation.Date() let time = endDate.timeIntervalSince(startDate) print(time) print("Successful!") print(exportSession?.outputURL as Any) let mediaPath = exportSession?.outputURL?.path as String! print(mediaPath) self.uploadVideo(mediaPath! as String) / / / / default: break } }) }
// Upload video
func createBodyWithParamsVideo(_ parameters: [String: String]?, filePathKey: String?, mediaPath: Data, boundary: String) -> Data { var body = "" if let params = parameters { for (key, value) in params { body += "--\(boundary)\r\n" body += "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n" body += "\(value)\r\n" } } var filename = "" if imageSelected { filename = "video-\(uuid).mp4" } let mimetype = "video/mp4" body += "--\(boundary)\r\n" body += "Content-Disposition: form-data; name=\"\(filePathKey!)\"; filename=\"\(filename)\"\r\n" body += "Content-Type: \(mimetype)\r\n\r\n" body += String(data: mediaPath, encoding: .utf8)! body += "\r\n" body += "--\(boundary)--\r\n" return Data(body.utf8) } / func uploadVideo(_ mediaPath: String) { let id = user!["id"] as! String uuid = UUID().uuidString let url = URL(string: "http:/ var request = URLRequest(url: url) request.httpMethod = "POST" let param = [ "id" : id, "uuid" : uuid ] print("just passed videopost page") / let boundary = "Boundary-\(UUID().uuidString)" request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") / / / let imageData = Data() / request.httpBody = createBodyWithParamsVideo(param, filePathKey: "file", mediaPath: imageData, boundary: boundary) / URLSession.shared.dataTask(with: request) { data, response, error in / DispatchQueue.main.async(execute: { if error == nil { do { / let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary / guard let parseJSON = json else { print("Error while parsing") return } / let message = parseJSON["message"] / if message != nil { / self.postBtn.alpha = 0.4 self.imageSelected = false / self.tabBarController?.selectedIndex = 4 } } catch { / DispatchQueue.main.async(execute: { let message = "\(error)" appDelegate.infoView(message: message, color: colorSmoothRed) }) return } } else { / DispatchQueue.main.async(execute: { let message = error!.localizedDescription appDelegate.infoView(message: message, color: colorSmoothRed) }) return } }) }.resume() }
Hello techgirl08,
It looks like, on line 53, you are creating an empty Data object named "imageData". Then, you pass that to "createBodyWithParamsVideo" as the "mediaPath" parameter. That would explain the zero byte data. Was it supposed to be the actual image data?
Then you convert that zero byte data object into a string as the body. You don't need to do that. HTTP can handle binary data.
I strongly suggest looking for some library to properly construct a form upload request. Those can be tricky. If your data is very large, those are really, really tricky. You may need to deal with chunked data, interruptions, and resuming transfers.
An even better idea would be to use a REST server on the other end. Then you could do simpler PUT request as well as things like DELETE. There are many examples of REST servers in PHP or Node.