录制的iPhone X视频上传到PHP服务器后将无法播放



根据建议解决方案修订的代码,但仍无法正常工作。视频将上传为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)rn"
            body += "Content-Disposition: form-data; name="(key)"rnrn"
            body += "(value)rn"
        }
    }
    var filename = ""
    if imageSelected {
        filename = "video-(uuid).mp4"
    }
    let mimetype = "video/mp4"
    body += "--(boundary)rn"
    body += "Content-Disposition: form-data; name="(filePathKey!)"; filename="(filename)"rn"
    body += "Content-Type: (mimetype)rnrn"
    body += String(data: videoData, encoding: .utf8)!
    body += "rn"
    body += "--(boundary)--rn"
    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()
}

好吧,我做了一些代码来实际编码和上传视频。不幸的是,在URLSession中进行multipart/form-data实际上很难,因此我使用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.")
    })
}

视频上传(使用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)
        }
    }
}

我希望这对您有用,随时提出。

相关内容

最新更新