基于Python Hyper實現(xiàn)Http2的multipart/form-data數(shù)據(jù)上傳

Http/2雖然推出已經(jīng)不短了,但目前整體的使用率并不高,對應的支持庫也并不理想,目前主要的支持庫可以參考:https://github.com/http2/http2-spec/wiki/Implementations

針對也Python,目前可選的庫好像只有Hyper(http://hyper.readthedocs.io/en/latest/)。Hyper在官網(wǎng)當中聲稱對Requests有很好的支持,可以將Hyper集成到Request中,完成Requests對Http/2的支持,

requests集成hyper

但在實際開發(fā)中并不理想。比如,默認Request的請求超時時間是無限的,但通過集成Hyper來使用Requests進行get請求時,還是會出現(xiàn)超時的情況,所以,對于Http/2的開發(fā),不建議使用這種方式。

開發(fā)背景

在開發(fā)“才權的AI小助手”過程中,進行了DuerOS云端接口的接入,DuerOS的云端接口是基于Http/2的,而且,需要使用multipart/form-data進行當前狀態(tài)和語音數(shù)據(jù)流的上傳。

DuerOS語音狀態(tài)和數(shù)據(jù)上傳格式

http://open.duer.baidu.com/doc/dueros-conversational-service/device-interface/voice-input_markdown

面臨問題和解決方案

Hyper中并沒有專門的接口用來實現(xiàn)multipart/form-data類型數(shù)據(jù)的上傳,而是直接接收已經(jīng)序列化后的body數(shù)據(jù)。

Hyper數(shù)據(jù)上傳

針對這種情況,我們可以Http協(xié)議的報文定義,來定制body的內容,最終實現(xiàn)Hyper對multipart/form-data類型數(shù)據(jù)上傳的支持。

數(shù)據(jù)報文格式

示例代碼

對于DuerOS的語音請求,需要講語音狀態(tài)(Json串)和語音數(shù)據(jù)(PCM音頻流)以multipart/form-data的形式進行上傳。這里我們通過get_multipart_formed_data()方法來定制body內容,

'''
    msg_id:消息ID(messageId字段)
    dialog_req_id:對話ID(dialogRequestId字段)
    format:語音數(shù)據(jù)格式(format字段)
    data:語音raw data(pcm數(shù)據(jù)流)
'''
def get_multipart_formed_data(self, msg_id, dialog_req_id, format, data):
    
    event={'clientContext':['ai.dueros.device_interface.alerts.AlertsState','ai.dueros.device_interface.audio_player.PlaybackState','ai.dueros.device_interface.speaker_controller.VolumeState','ai.dueros.device_interface.voice_output.SpeechState'], \
   'event':{'header':{'namespace':'ai.dueros.device_interface.voice_input', \
                      'name':'ListenStarted', \
                      'messageId':msg_id, \
                      'dialogRequestId':dialog_req_id}, \
            'payload':{'format':format}}}
    
    event=json.dumps(event)
    
    post_data1=[]
    
    # ListenStarted事件
    post_data1.append('--'+boundary)
    post_data1.append('Content-Disposition: form-data; name="metadata"')
    post_data1.append('Content-Type: text/plain; charset=utf-8')
    post_data1.append('')
    post_data1.append(event)
#     post_data1.append('--'+boundary+'--')# test
    post_data1.append('')
    
#     return crlf.join(post_data1).encode('utf-8')# test
    
#     # Audio data
    post_data1.append('--'+boundary)
    post_data1.append('Content-Disposition: form-data; name="audio"')
    post_data1.append('Content-Type: application/octet-stream')
    post_data1.append('')
     
    body1=crlf.join(post_data1).encode('utf-8')
     
    body2=data
     
    post_data3=[]
    post_data3.append('--'+boundary+'--')
    post_data3.append('')
    body3=crlf.join(post_data3).encode('utf-8')
     
    upload_data=body1+b'{0}'.format(crlf)+body2+b'{0}'.format(crlf)+body3
     
    return upload_data

語音狀態(tài)和數(shù)據(jù)上傳,

'''
    msg_id:消息ID(messageId字段)
    dialog_req_id:對話ID(dialogRequestId字段)
    format:語音數(shù)據(jù)格式(format字段)
    data:語音raw data(pcm數(shù)據(jù)流)
'''
def voice_raw_data_upload(self, msg_id, dialog_req_id, format, data):
    post_body=self.get_multipart_formed_data(msg_id, dialog_req_id, format, data)
    self.conn.request('POST', path_upload_voice_data, headers=self.headers, body=post_body)
    resp = self.conn.get_response()
      
    return resp.read()

完整代碼參考

完整的代碼可以參考
《Eddy的AI小助手-百度DuerOS模塊接入(23)》

博客地址

http://caiquanliu.github.io

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容