无法使用 Alamofire 通过服务器上传音频文件

Posted

技术标签:

【中文标题】无法使用 Alamofire 通过服务器上传音频文件【英文标题】:Can't upload audio file through server using Alamofire 【发布时间】:2016-11-13 15:11:07 【问题描述】:
class VoiceRecogViewController: UIViewController, AVAudioPlayerDelegate, AVAudioRecorderDelegate 


var audioPlayer: AVAudioPlayer?
var audioRecorder: AVAudioRecorder?
var error: NSError?
var soundFileURL: URL?
var soundFilePath: String = ""
var data : NSData?

@IBOutlet weak var startRocordButton: UIButton!
@IBOutlet weak var stopRecordButton: UIButton!
@IBOutlet weak var playRecordButton: UIButton!
@IBOutlet weak var continueButton: UIButton!

override func viewDidLoad() 

    super.viewDidLoad()
    playRecordButton.isEnabled = false
    stopRecordButton.isEnabled = false

    let dirPaths =
        NSSearchPathForDirectoriesInDomains(.documentDirectory,
                                            .userDomainMask, true)

    let docsDir = dirPaths[0]
    soundFilePath = (docsDir as NSString).appendingPathComponent("sound.wav")
    soundFileURL = URL(fileURLWithPath: soundFilePath)

    let recordSettings = [AVEncoderAudioQualityKey: AVAudioQuality.min.rawValue,
                          AVEncoderBitRateKey: 16,
                          AVNumberOfChannelsKey: 2,
                          AVSampleRateKey: 44100.0] as [String : Any]

    let audiosession = AVAudioSession.sharedInstance()
    try! audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: [])
    try! audioSession.setActive(true)

    if let err = error 
        print("audioSession error: \(err.localizedDescription)")
    

    do 
        audioRecorder = try AVAudioRecorder(url: soundFileURL!,
                                            settings: recordSettings as [String : AnyObject])
     catch 
        audioRecorder = nil
    

    audioRecorder?.prepareToRecord()

    // Do any additional setup after loading the view.


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.



@IBAction func startRecord(_ sender: AnyObject) 
    if audioRecorder?.isRecording == false 
        playRecordButton.isEnabled = false
        startRocordButton.isEnabled = false
        stopRecordButton.isEnabled = true
        audioRecorder?.record()
    


@IBAction func stopRecord(_ sender: AnyObject) 
    stopRecordButton.isEnabled = false
    playRecordButton.isEnabled = true
    startRocordButton.isEnabled = true

    if audioRecorder?.isRecording == true 
        audioRecorder?.stop()
     else 
        audioPlayer?.stop()
    


@IBAction func playRecord(_ sender: AnyObject) 
    if audioRecorder?.isRecording == false 
        stopRecordButton.isEnabled = true
        startRocordButton.isEnabled = false
    

    do 
        try audioPlayer = AVAudioPlayer(contentsOf: soundFileURL!)
        audioPlayer?.delegate = self
        audioPlayer?.prepareToPlay()
        audioPlayer?.play()
     catch 
        print("audioPlayer error")
    


@IBAction func continueRegist(_ sender: AnyObject) 

    let headers: HTTPHeaders = ["Authorization": "Token ___(**token**)_____",
                                "Accept": "application/json"]

    data = NSData (contentsOf: soundFileURL!)

    let parameters: Parameters = ["from_account_id": "3",
                                  "to_account_id": "4",
                                  "file": data!,
                                 ]

    let URL = "http://leaofımjpüsmfweüdıpckfw"

            Alamofire.request(URL, method: .put, parameters: parameters, headers: headers).responseJSON  response in
                if let data = response.result.value 
                    print(data)
                
            

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) 
    startRocordButton.isEnabled = true
    stopRecordButton.isEnabled = false


func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) 
    print("Audio Play Decode Error")


func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) 


func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) 
    print("Audio Record Encode Error")

我在上面分享了我的代码。这里的重点是将音频录制为 .wav,在应用程序中播放,在 continueRegist 部分我想用 alamofire 调用 put 方法并将音频上传到我们的 amazons3server。录制和播放音频部分完全正常。问题出在 continueRegist 方法中。调用该方法后,我得到了正确的响应,看起来很成功。然后,我从我们的 s3 服务器的 url 中检查它。音频似乎已上传,但当我下载并播放时,它无法正常工作。我无法弄清楚问题出在哪里。

另外,当我尝试从 Postman 上传并选择文件并提供正确的表单数据信息时,我可以听到上传到 s3 服务器的声音。这里可能有什么问题?

下面你可以通过 Postman 找到我的请求:

身体:

我在截屏时忘记选择文件,但它只是一个 .wav 文件。

标题

如果您不满意,请随时问我。

希望你能帮助我。

谢谢!

【问题讨论】:

你遇到了什么错误? 没有给出错误。服务器和亚马逊 s3 都接受我的请求并将文件放入 s3。由于某些原因,我不理解并相信那里的问题,当我从 s3 链接手动下载文件时,我的文件无法播放。请注意,我可以通过 Postman 发送的下载来播放音频文件。构造主体可能存在问题。 所以问题是当你下载音频时不要上传它,因为你可以通过 Postman 播放音频 您能否编辑您的问题并添加邮递员的屏幕截图 是的,马上,给我一点时间 【参考方案1】:

问题出在您的 Alamofire 请求中:您正在使用 JSON 中的音频数据构建 JSON。但是,您可以在 Postman 中检查请求是多部分表单数据的 HTTP 代码(右上角/下发送)。

如何实现多部分 Alamofire: 它应该类似于我不确定appendBodyPart 语句的内容。他们取决于你的情况

let audioData: NSData = ...//should be loaded from the file

Alamofire.Manager.upload(.PUT,
                          URL,
                          headers: headers,
                          multipartFormData:  multipartFormData in
                            multipartFormData.appendBodyPart(data: "3".dataUsingEncoding(NSUTF8StringEncoding), name: "from_account_id")
                            multipartFormData.appendBodyPart(data: "4".dataUsingEncoding(NSUTF8StringEncoding), name: "to_account_id")
                            multipartFormData.appendBodyPart(data: audioData, name: "file", fileName: "file", mimeType: "application/octet-stream")
                            ,
                          encodingCompletion:  encodingResult in
                            switch encodingResult 

                            case .Success(let upload, _, _):
                                upload.responseJSON  response in

                                

                            case .Failure(let encodingError):
                                // Error while encoding request:
                            
)

【讨论】:

好的,我会尝试一下您的建议并尝试使其发挥作用,然后会回复。非常感谢 您能否也将身份验证标头添加到您的答案中作为示例? 我不在笔记本电脑上。编辑实际上删除了 header 参数。应该有一个方法接受参数请求类型(.Post/.put..)和标头 是的,根据您的建议和 Alamofire 官方的建议,我尝试了很多,但无法正常工作。感觉就像我接近解决方案,但不起作用。 是的,你非常接近:邮递员中的 http 代码它说什么?可以加个截图吗【参考方案2】:

以下是上传音频文件的代码:

let uploadAudioURL = "http://<your post API url>"



    let header : [String:String] = [
        "Authorization" : "<your authorisation token>"
    ]

    let voiceData = try? Data(contentsOf: <url of audio file to upload>)


    let params : [String:String] = [
        "length" : "39000",
        "title" : "Trying to upload",
    ]

    Alamofire.upload(
        multipartFormData:  (multipartFormData) in
            multipartFormData.append(voiceData!, withName: "voice", fileName: "TempRecordedAudioFile", mimeType: "audio/m4a")
            for (key, value) in params 
                multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key)
            
    ,
        usingThreshold : SessionManager.multipartFormDataEncodingMemoryThreshold,
        to : uploadAudioURL,
        method: .post,
        headers: header) (result) in

            switch result 
            case .success(let upload, _, _):

                upload.uploadProgress(closure:  (Progress) in
                    print("Upload Progress: \(Progress.fractionCompleted)")
                )

                upload.responseJSON  response in

                    if let JSON = response.result.value 
                        print("Response : ",JSON)

                    
                

            case .failure(let encodingError):
                print(encodingError)
            

    

希望它会有所帮助。

使用上述代码sn-p的一些注意事项:

    确保提供上传 URL 添加所需的授权标头详细信息。 在 Alamofire.upload 语句中确保 withName 设置为您的上传 API 参数名称。我用了voice,你可能有不同的参数名称。 upload.uploadProgress() 是可选的。如果不需要,可以将其删除。

【讨论】:

以上是关于无法使用 Alamofire 通过服务器上传音频文件的主要内容,如果未能解决你的问题,请参考以下文章

Alamofire +ObjectMapper模型: 上传音频。

无法在服务器上上传数据

通过 Alamofire 上传文件并使用 Slim PHP 在服务器端检索它

如何使用操作查询发出多个 Alamofire 请求

如何使用 alamofire 上传多个文件并显示进度?

无法使用 alamofire 4 上传带参数的图像?