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的支持易迹,
但在實際開發(fā)中并不理想宰衙。比如,默認Request的請求超時時間是無限的睹欲,但通過集成Hyper來使用Requests進行get請求時供炼,還是會出現(xiàn)超時的情況一屋,所以,對于Http/2的開發(fā)袋哼,不建議使用這種方式冀墨。
開發(fā)背景
在開發(fā)“才權的AI小助手”過程中,進行了DuerOS云端接口的接入涛贯,DuerOS的云端接口是基于Http/2的诽嘉,而且,需要使用multipart/form-data進行當前狀態(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ù)。
針對這種情況稀余,我們可以Http協(xié)議的報文定義悦冀,來定制body的內容,最終實現(xiàn)Hyper對multipart/form-data類型數(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)》