RFC7578(淘汰了RFC2388)定義了multipart/form-data類型寨蹋,當(dāng)用戶在Web頁面上提交表單時(shí),這種數(shù)據(jù)類型通常通過HTTP進(jìn)行傳輸丈攒。如今褥实,它往往被JSON編碼的payload(負(fù)載)所取代;盡管如此遍希,它仍然被廣泛使用等曼。
雖然你可以用Python原生解碼使用JSON創(chuàng)建的HTTP主體請(qǐng)求(這多虧了JSON模塊),但是對(duì)于multipart/form-data卻沒有這樣的方法孵班∩媸蓿考慮到這種格式的歷史悠久,這幾乎是難以理解的篙程。
有很多種方法可以對(duì)這種格式進(jìn)行編碼和解碼。像requests之類的庫在不引起你注意的情況下已經(jīng)原生地支持這種特性别厘,大多數(shù)Web服務(wù)器框架(比如Django或Flask)也是如此虱饿。
然而,在某些情況下触趴,你可能需要自己對(duì)這種格式進(jìn)行編碼或解碼氮发。在這些情況下,通過添加外部依賴來處理編解碼冗懦,不是一個(gè)可接受的選項(xiàng)(比如嵌入式設(shè)備上沒有網(wǎng)爽冕,無法pip)。
編碼
multipart/form-data格式非常容易理解披蕉,可以概括為一種編碼鍵和值列表的簡單方法颈畸,即,一種可移植的序列化字典的方法没讲。
Python中沒有任何東西可以生成這樣的編碼眯娱。這種格式非常簡單,由鍵和值組成爬凑,并由一個(gè)隨機(jī)的邊界分隔符包圍徙缴。分隔符必須作為 Content-Type的一部分進(jìn)行傳遞,以便解碼器能夠解碼表單數(shù)據(jù)嘁信。
urllib3中有完成這項(xiàng)工作的一個(gè)簡單實(shí)現(xiàn)于样。我們?cè)谶@個(gè)簡單的實(shí)現(xiàn)中對(duì)它總結(jié)一下:
你可以通過傳遞一個(gè)字典來使用這種格式,其中鍵和值都是字節(jié)潘靖。例如:
它會(huì)返回:
你可以在HTTP應(yīng)答頭部Content-Type中使用返回的內(nèi)容類型穿剖。注意,這種格式是用于表單的:它也可以用于電子郵件秘豹。
你說的是電子郵件嗎?
使用email進(jìn)行編碼
是的携御,電子郵件通常是使用MIME編碼的,MIME由另一個(gè)RFC—RFC2046—定義。事實(shí)證明啄刹,multipart/form-data只是一種特殊的MIME格式涮坐,如果你有實(shí)現(xiàn)MIME處理的代碼,那么使用它來實(shí)現(xiàn)這種格式是很容易的誓军。
對(duì)我們來說幸運(yùn)的是袱讹,Python標(biāo)準(zhǔn)庫附帶了一個(gè)模塊,該模塊可以處理它:email.mime昵时。我告訴過你它被電子郵件大量使用—我猜這就是為什么他們會(huì)把處理代碼放在email子包中捷雕。
這里有一段代碼,它使用幾行代碼來處理multipart/form-data :
使用這段代碼會(huì)返回以下結(jié)果:
與我們的第一個(gè)實(shí)現(xiàn)相比壹甥,這個(gè)方法有幾個(gè)優(yōu)點(diǎn):
- 它為每個(gè)添加的MIME部分處理Content-Type救巷。我們可以添加其他數(shù)據(jù)類型,而不僅僅是text/plain句柠,就像在第一個(gè)版本中顯式所做的那樣浦译。我們還可以指定文本數(shù)據(jù)的字符集(編碼方式)。
- 通過使用經(jīng)過廣泛測(cè)試的Python標(biāo)準(zhǔn)庫溯职,它很可能會(huì)更加健壯精盅。
在這種情況下,主要的缺點(diǎn)是內(nèi)容中包含了Content-Type頭部谜酒。在處理HTTP時(shí)叹俏,這是有問題的,因?yàn)檫@需要作為HTTP頭部的一部分被發(fā)送僻族,而不是作為負(fù)載的一部分粘驰。
從email.generator構(gòu)建一個(gè)完成這一任務(wù)的特定的生成器是可能的。我將把它留給你們做練習(xí)鹰贵,讀者們晴氨。
解碼
我們必須能夠使用相同的 email 包來解碼我們的編碼數(shù)據(jù),對(duì)嗎?事實(shí)證明是這樣的碉输,我們可以通過一段這樣的代碼來實(shí)現(xiàn):
使用上面的示例數(shù)據(jù)籽前,這段代碼會(huì)返回:
超級(jí)棒,對(duì)嗎敷钾?
這個(gè)故事的寓意是枝哄,你永遠(yuǎn)不要低估標(biāo)準(zhǔn)庫的威力。雖然在你的依賴項(xiàng)列表中添加一行代碼很容易阻荒,但是如果你深入研究一下Python為你提供了什么挠锥,你會(huì)發(fā)現(xiàn)你并不總是需要這樣做!
英文原文:https://julien.danjou.info/handling-multipart-form-data-python/
譯者:野生大熊貓
注:我這有個(gè)學(xué)習(xí)Python基地,里面有很多學(xué)習(xí)資料侨赡,感興趣的+Q群:895817687