Sending audio from a Swift App to PHP Server, and somewhere the audio is lost

后端 未结 2 1285
攒了一身酷
攒了一身酷 2021-01-06 18:58

I am making an App in Swift that records some audio and then sends that recording to my PHP server.

The App records the audio clip fine (it can be played back with n

相关标签:
2条回答
  • 2021-01-06 19:43

    here is the swift 4 ~ 5 code. creat new a button(buttonLabel in code) on ViewController and link button action to @IBAction and @IBOutlet [Hold to recode and release to upload audio file]

    import UIKit
    import AVFoundation
    
    class ViewController2: UIViewController, AVAudioRecorderDelegate{
        
        var recordingSession: AVAudioSession!
        var audioRecorder: AVAudioRecorder!
        var audioPlayer: AVAudioPlayer!
        var numberOfRecords = 0
        
    
        @IBOutlet weak var buttonLabel: UIButton!
        let E_401 = "E_401"
        let DATABASE_PATH = "http://<IP_address_of_PHP_server>/YourPrjectName/"
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // Set the recognizer to recognize the button action
            let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(record))
            longPressRecognizer.minimumPressDuration = 0
            buttonLabel.addGestureRecognizer(longPressRecognizer)
            
            // Setting up session
            recordingSession = AVAudioSession.sharedInstance()
            
            
            // Get permission from user to use mic
            AVAudioSession.sharedInstance().requestRecordPermission{ (hasPermission) in
                if hasPermission
                {print ("ACCEPTED")}
            }
        }
        
        
        @IBAction func record(_ gestureRecognizer: UILongPressGestureRecognizer) {
            
            // Check if we have an active recorder
            if (gestureRecognizer.state == .began) && (audioRecorder == nil) {
                // Increase +1 total number of recordings for every new recording made
                
                self.numberOfRecords += 1
                
                // Setting filename and settings
                let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
                let settings = [
                    AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
                    AVSampleRateKey: 12000,
                    AVNumberOfChannelsKey: 1,
                    AVEncoderAudioQualityKey: AVAudioQuality.medium.rawValue
                ]
                
                do
                {
                    // Start audio recording
                    buttonLabel.setTitle("Recording...", for: .normal)
                    audioRecorder = try AVAudioRecorder(url: filename, settings: settings)
                    audioRecorder.delegate = self
                    audioRecorder.record()
                }
                catch
                {
                    // Catch for errors
                    displayAlert(title: "Oops!", message: "Recording failed")
                    
                }
                
            } else if gestureRecognizer.state == .ended && (audioRecorder != nil)
                
            {
                // Stopping audio recording
                buttonLabel.setTitle("Start Recording", for: .normal)
                audioRecorder.stop()
                audioRecorder = nil
                
                
                
                do {
                    let filename = getDirectory().appendingPathComponent("\(numberOfRecords).m4a")
                    let recording: NSData = try NSData(contentsOf: filename)
                    self.uploadFile(fileData: recording as Data, fileName: "\(numberOfRecords).m4a"){
                        (fileURL, e) in
                        if e == nil {
                            print("FILE URL: " + fileURL!)
                        }
                    }
                    
                } catch {
                    print("Unexpected <<<<<<<<<<<<<<>>>>>>>>>>>>>> error: \(error)")
                }
    
            }
        }
        
        // Function that gets path to directory
        func getDirectory () -> URL
        {
            let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
            let documentDirectory = paths[0]
            
            return documentDirectory
        }
        
        // Function that displays an alert
        func displayAlert(title:String, message:String)
        {
            let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "dismiss", style: .default, handler: nil))
            present(alert, animated: true, completion: nil)
        }
        
        
        func uploadFile(fileData:Data, fileName:String , completion: @escaping (_ fileURL:String?, _ error:String?) -> Void) {
            let recId = "\(numberOfRecords)"
            print("FILENAME: \(fileName)")
            let request = NSMutableURLRequest()
    
            let boundary = "--------14737809831466499882746641449----"
            let beginningBoundary = "--\(boundary)"
            let endingBoundary = "--\(boundary)--"
            let contentType = "multipart/form-data;boundary=\(boundary)"
            
            
            
            request.url = URL(string: DATABASE_PATH + "catch.php")
    //        catch.php is php script on server
            request.httpShouldHandleCookies = false
            request.timeoutInterval = 60
            request.httpMethod = "POST"
            request.setValue(contentType, forHTTPHeaderField: "Content-Type")
            let body = NSMutableData()
            body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
            body.append("Content-Disposition: form-data; name=\"fileName\"\r\n\r\n".data(using: String.Encoding.utf8)!)
            body.append("\(fileName)\r\n".data(using: String.Encoding.utf8)!)
            body.append("--\(boundary)\r\n".data(using: String.Encoding.utf8)!)
            body.append("Content-Disposition: form-data; name=\"file\"; filename=\"file\"\r\n".data(using: String.Encoding.utf8)!)
            
            body.append(("\(beginningBoundary)\r\n" as NSString).data(using: String.Encoding.utf8.rawValue)!)
            body.append(("Content-Type: application/octet-stream\r\n\r\n" as NSString).data(using: String.Encoding.utf8.rawValue)!)
    
            body.append(fileData)
            body.append("\r\n".data(using: String.Encoding.utf8)!)
            
            
            body.append("--\(boundary)--\r\n".data(using: String.Encoding.utf8)!)
            request.addValue(contentType, forHTTPHeaderField: "Content-Type")
    //        request.addValue(recId, forHTTPHeaderField: "REC-ID")
            request.httpBody = body as Data
            
            
            let session = URLSession.shared
            let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
                guard let _:Data = data as Data?, let _:URLResponse = response, error == nil else {
                    DispatchQueue.main.async { completion(nil, error!.localizedDescription) }
                    return
                }
                if let response = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue)) {
                    print("XSUploadFile -> RESPONSE: " + self.DATABASE_PATH + response)
                    DispatchQueue.main.async { completion(self.DATABASE_PATH + response, nil) }
                    
                    // NO response
                } else { DispatchQueue.main.async { completion(nil, self.E_401) } }// ./ If response
            }; task.resume()
        }
    }
    
    0 讨论(0)
  • 2021-01-06 19:48

    Your PHP code is mostly fine. The $_FILES part is OK, but php://input is not available with enctype="multipart/form-data".

    The problem is with how you are generating the HTTP request in your Swift code. Mainly the HTTP headers. When creating the headers for multipart data, the pattern is this (if we choose AAAAA to be our boundary):

    • Our chosen boundary: "AAAAA"
    • Content Type = "multipart/form-data;boundary=AAAAA"
    • Beginning Bounary = --AAAAA
    • Ending Boundary = --AAAAA--

    So by fixing up your code a little bit:

    // This was your main problem
    let boundary = "--------14737809831466499882746641449----"
    let beginningBoundary = "--\(boundary)"
    let endingBoundary = "--\(boundary)--"
    let contentType = "multipart/form-data;boundary=\(boundary)"
    
    // recordedFilePath is Optional, so the resulting string will end up being 'Optional("/path/to/file/filename.m4a")', which is wrong.
    // We could just use currentFilename if we wanted
    let filename = recordedFilePath ?? currentFilename
    var header = "Content-Disposition: form-data; name=\"\(currentFilename)\"; filename=\"\(recordedFilePath!)\"\r\n"
    
    var body = NSMutableData()
    body.appendData(("\(beginningBoundary)\r\n" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData((header as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData(("Content-Type: application/octet-stream\r\n\r\n" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
    body.appendData(recording!) // adding the recording here
    body.appendData(("\r\n\(endingBoundary)\r\n" as NSString).dataUsingEncoding(NSUTF8StringEncoding)!)
    
    var request = NSMutableURLRequest()
    request.URL = sendToURL
    request.HTTPMethod = "POST"
    request.addValue(contentType, forHTTPHeaderField: "Content-Type")
    request.addValue(recId, forHTTPHeaderField: "REC-ID") // recId is defined elsewhere
    request.HTTPBody = body
    

    In case you run into things like this again, when debugging networking code like this, I like to use tools that let me inspect the HTTP Network data being transferred. I personally like HTTPScoop because its simple, but you can also use BurpSuite or Charles

    I just ran your code through, and compared the HTTP traffic with what happened when I made the request with curl

    curl -X POST http://localhost/\~cjwirth/catch.php -F "file=@Untitled.m4a"
    
    0 讨论(0)
提交回复
热议问题