上传到PHP Server后,录制的iPhone X视频无法播放

时间:2017-12-20 16:41:28

标签: php ios swift video

代码根据建议解决方案修改但仍无法正常工作。视频上传为0字节。我有一个应用程序,用户应该可以通过手机录制视频,以及何时使用视频'选择它将视频上传到我们的PHP服务器。文件正在成功上传,并根据显示的大小不为空。然而,当我在应用程序中播放视频或甚至直接通过浏览器播放时,它都不会播放。回放代码没有问题,因为我已经硬编码了其他网站上其他视频的链接,而且工作正常。代码如下,非常感谢任何帮助。

// Finished recording a video
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    print("Got a video")

    if let pickedVideo:URL = (info[UIImagePickerControllerMediaURL] as? URL) {
        // Save video to the main photo album
        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"
        }
        // Save the video to the app directory so we can play it later
        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: {
            // Anything you want to happen when the user saves an video
            self.encodeVideo(dataPath: dataPath)
            self.uploadVideo(videoData!)



        })
    } }

// MP4视频转换

  func encodeVideo(dataPath: URL){
    let avAsset = AVURLAsset(url: dataPath)
    let startDate = Date()
    let exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)

    let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    let myDocPath = NSURL(fileURLWithPath: docDir).appendingPathComponent("temp.mp4")?.absoluteString

    let docDir2 = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as NSURL

    let filePath = docDir2.appendingPathComponent("rendered-Video.mp4")
    //uploadVideo(filePath)
    //self.encodeVideo(dataPath: dataPath)
    deleteFile(filePath!)


    if FileManager.default.fileExists(atPath: myDocPath!){
        do{
            try FileManager.default.removeItem(atPath: myDocPath!)

        }catch let error{
            print(error)
        }
    }
    //self.uploadVideo((myDocPath as AnyObject) as! URL)

    exportSession?.outputURL = filePath
    exportSession?.outputFileType = AVFileType.mp4
    exportSession?.shouldOptimizeForNetworkUse = true

    let start = CMTimeMakeWithSeconds(0.0, 0)
    let range = CMTimeRange(start: start, duration: avAsset.duration)
    exportSession?.timeRange = range

    exportSession!.exportAsynchronously{() -> Void in
        switch exportSession!.status{
        case .failed:
            print("\(exportSession!.error!)")
        case .cancelled:
            print("Export cancelled")
        case .completed:
            let endDate = Date()
            let time = endDate.timeIntervalSince(startDate)
            print(time)
            print("Successful")
            print(exportSession?.outputURL ?? "")
        default:
            break
        }

    }
}
func deleteFile(_ filePath:URL) {
    guard FileManager.default.fileExists(atPath: filePath.path) else {
        return
    }

    do {
        try FileManager.default.removeItem(atPath: filePath.path)
    }catch{
        fatalError("Unable to delete file: \(error) : \(#function).")
    }
}

//创建参数

   func createBodyWithParams(_ parameters: [String: String]?, filePathKey: String?, videoData: 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: videoData, encoding: .utf8)!
    body += "\r\n"

    body += "--\(boundary)--\r\n"

    return Data(body.utf8)

}

//函数向PHP发送请求以上传文件  func uploadVideo(_ videoData:Data){

    let id = user!["id"] as! String
    uuid = UUID().uuidString


    let url = URL(string: "http://www.foo.com/videoposts.php")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    let param = [
        "id" : id,
        "uuid" : uuid
    ]

    // body
    let boundary = "Boundary-\(UUID().uuidString)"
    request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    // if picture is selected, compress it by half
    let imageData = Data()


    // ... body
    request.httpBody = createBodyWithParams(param, filePathKey: "file", videoData: imageData, boundary: boundary)

    // launch session
    URLSession.shared.dataTask(with: request) { data, response, error in

        // get main queu to communicate back to user
        DispatchQueue.main.async(execute: {


            if error == nil {

                do {

                    // json containes $returnArray from php
                    let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary

                    // declare new var to store json inf
                    guard let parseJSON = json else {
                        print("Error while parsing")
                        return
                    }

                    // get message from $returnArray["message"]
                    let message = parseJSON["message"]

                    // if there is some message - post is made
                    if message != nil {

                        // reset UI

                        self.postBtn.alpha = 0.4
                        self.imageSelected = false

                        // switch to another scene
                        self.tabBarController?.selectedIndex = 4

                    }

                } catch {

                    // get main queue to communicate back to user
                    DispatchQueue.main.async(execute: {
                        let message = "\(error)"
                        appDelegate.infoView(message: message, color: colorSmoothRed)
                    })
                    return

                }

            } else {

                // get main queue to communicate back to user
                DispatchQueue.main.async(execute: {
                    let message = error!.localizedDescription
                    appDelegate.infoView(message: message, color: colorSmoothRed)
                })
                return

            }


        })

        }.resume()

}

1 个答案:

答案 0 :(得分:1)

好的,我确实编码了一些实际编码和上传视频的内容。 不幸的是,在multipart/form-data中执行URLSession实际上非常困难,所以我使用Alamofire来实际上传视频。

这是部分代码:

UIImagePickerControllerDelegate (这是您应该修改的,请查看 // TODO 评论)

// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    print("Cancelled video operation.")
    dismiss(animated: true, completion: nil)
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    guard let mediaType = info[UIImagePickerControllerMediaType] as? String else {
        print("Error with media type. Cancelling.")
        dismiss(animated: true, completion: nil)
        return;
    }

    print("Media type: \(mediaType)")

    if ( mediaType == "public.movie" ) { // Video selected
        let videoURL: URL
        if info[UIImagePickerControllerMediaURL] != nil {
            videoURL = info[UIImagePickerControllerMediaURL] as! URL
        }
        else {
            videoURL = info[UIImagePickerControllerReferenceURL] as! URL
        }


        if ( picker.sourceType == .camera ) {
            // The original video came from the camera, so it's considered new
            // Save it to the photo library
            saveVideo(url: videoURL, albumName: "MyApp")
        }

        // Dismiss the media picker and then re-encode the video
        dismiss(animated: true) {
            self.exportVideoToMP4(url: videoURL) { (exportedVideoURL) in

                guard let tempURL = exportedVideoURL else {
                    print("ERROR: Unknown error. The exported video URL is nil.")
                    return
                }

                print("Temporary video successfully exported to: \(tempURL.absoluteString)")





                // TODO: Add your own POST parameters
                let uuid = UUID().uuidString
                let params = [
                    "uuid" : uuid,
                    "user" : "myUserNameOrWhatever"
                ]


                // TODO: Change the parameters for uploading
                self.upload(    to: "http://yourweb.com/uploadVideo.php", // The URL to send the upload to
                                videoURL: tempURL, // The file URL of the temporary video file
                                parameters: params, // The POST parameters you want to send along with the upload
                                fileName: "vid-\(uuid).mp4", // The filename you want the server to receive.
                                fieldName: "video_file" // This is "name" from <input type="file" name="video_file" ... /> in HTML
                ) { (response) in

                    guard let resp = response else {
                        print("ERROR: Empty or unrecognizable response from server.")
                        return
                    }

                    print("Video uploaded. RESPONSE: \(resp)")

                    //: TODO Parse the server response after uploading
                }

            }
        }
    }
}

照片库辅助方法

// MARK: Photo Library

func saveVideo(url: URL, albumName: String) {

    // Check authorization status before trying to save the video
    switch PHPhotoLibrary.authorizationStatus() {

    case .notDetermined:
        PHPhotoLibrary.requestAuthorization() { (status) in
            switch status {
            case .authorized:
                self.saveVideo(url: url, albumName: albumName) // Re-try to save the video after authorization
                return

            default:
                return
            }
        }



    case .authorized:
        // Save the video to the Photo Library here

        if let assetCollection = assetCollection(albumName: albumName) {

            // Asset collection exists, insert directly
            insertVideo(url: url, assetCollection: assetCollection)

        }
        else {

            // Asset collection doesn't exist, create it and then insert
            PHPhotoLibrary.shared().performChanges({

                PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: albumName)

            }, completionHandler: { (success, error) in
                guard success else {
                    print("ERROR: \(error!.localizedDescription)")
                    return
                }

                let createdAssetCollection = self.assetCollection(albumName: albumName)!
                self.insertVideo(url: url, assetCollection: createdAssetCollection)
            })

        }

        return



    default:
        // Not authorized
        print("Not authorized to save a video to the Photo Library.")
        return
    }


}


func assetCollection(albumName: String) -> PHAssetCollection? {
    let fetchOptions = PHFetchOptions()
    fetchOptions.predicate = NSPredicate(format:"title == '\(albumName)'")

    let fetchResult = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: fetchOptions)

    return fetchResult.firstObject
}


func insertVideo(url: URL?, assetCollection: PHAssetCollection) {
    guard let videoURL = url else {
        print("ERROR: The URL to insert into the Photo Library is empty.")
        return
    }

    PHPhotoLibrary.shared().performChanges({

        let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
        let assetPlaceholder = createAssetRequest?.placeholderForCreatedAsset
        let changeRequest = PHAssetCollectionChangeRequest(for: assetCollection)

        let enumeration: NSArray = [assetPlaceholder!]

        changeRequest?.addAssets(enumeration)

    }, completionHandler: { (success, error) in
        guard success else {
            print("ERROR: \(error!.localizedDescription)")
            return
        }

        print("Video saved successfully to the Photo Library album.")
    })
}

视频上传(使用从{CocoaPods安装的Alamofire

// MARK: Video upload
func upload(to uploadAddress: String, videoURL: URL, parameters: [String:Any]?, fileName: String, fieldName: String, _ completion: ((String?) -> Void)?) {

    Alamofire.upload(multipartFormData: { (multipartFormData) in

        // Add the video file (if data is correct)
        if let videoData = FileManager.default.contents(atPath: videoURL.path) {
            multipartFormData.append(videoData, withName: fileName)
        }

        // Add the post params (if available)
        if let params = parameters {
            for (key, value) in params {
                multipartFormData.append( (value as! String).data(using: .utf8)! , withName: key)
            }
        }

    }, to: uploadAddress)
    { (result) in

        switch result {
        case .success(let upload, _, _):
            upload.responseString { (response) in
                if let completionHandler = completion {
                    completionHandler(response.result.value)
                }
            }

        case .failure(let encodingError):
            print("ERROR: \(encodingError.localizedDescription)")
            if let completionHandler = completion {
                completionHandler(nil)
            }
        }

    }

}

AVFoundation (编码方法)

// MARK: AVFoundation
func exportVideoToMP4(url: URL, _ completion: @escaping ((URL?) -> Void)) {

    // Show some sort of indicator here, as this could take a while


    // Generate a temporary URL path to export the video
    let relativePath = "myAppTempVideoExport.mp4";
    let outputFilePath = NSTemporaryDirectory() + relativePath;

    print("Temp file path: \(outputFilePath)")


    // If there's any temp file from before at that path, delete it
    if FileManager.default.fileExists(atPath: outputFilePath) {
        do {
            try FileManager.default.removeItem(atPath: outputFilePath)
        }
        catch {
            print("ERROR: Can't remove temporary file from before. Cancelling export.")
            completion(nil)
            return
        }
    }


    // Export session setup
    let outputFileURL = URL(fileURLWithPath: outputFilePath)

    let asset = AVAsset(url: url) // Original (source) video

    // The original video codec is probably HEVC, so we'll force the system to re-encode it at the highest quality in MP4
    // You probably want to use medium quality if this video is intended to be uploaded (as this example is doing)
    if let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality) {

        exportSession.outputURL = outputFileURL
        exportSession.outputFileType = .mp4

        exportSession.exportAsynchronously {

            // Hide the indicator for the export session

            switch exportSession.status {
            case .completed:
                print("Video export completed.")
                completion(outputFileURL)
                return

            case .failed:
                print("ERROR: Video export failed. \(exportSession.error!.localizedDescription)")
                completion(nil)
                return

            case .cancelled:
                print("Video export cancelled.")
                completion(nil)
                return

            default:
                break
            }

        }

    }
    else {
        print("ERROR: Cannot create an AVAssetExportSession.")
        return
    }

}

现在,为了实现这一点,您显然必须录制视频,导入框架并指定视图控制器符合协议,因此:

import AVFoundation
import Photos
import Alamofire



class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    let imagePicker = UIImagePickerController()


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        imagePicker.sourceType = .camera
        imagePicker.delegate = self
        imagePicker.showsCameraControls = true
        imagePicker.allowsEditing = true
        imagePicker.mediaTypes = ["public.movie"]

        DispatchQueue.main.async {
            self.present(self.imagePicker, animated: true, completion: nil)
        }
    }
}

我希望这对你有用,随便问。