Prawidłowo sformatuj treść danych wieloczęściowych / formularzy
Piszę skrypt do przesyłania rzeczy, w tym plików za pomocąmultipart/form-data
typ zawartości zdefiniowany wRFC 2388. Na dłuższą metę staram się udostępnić prosty skrypt Pythonaładowanie pakietów binarnych dla github, co wiąże się z przesyłaniem danych w formie do Amazon S3.
To pytanie już zapytał, jak to zrobić, ale jak dotąd nie ma akceptowanej odpowiedzi ibardziej przydatne z dwóch odpowiedzi, na które obecnie wskazujete przepisy które z kolei budują całą wiadomość ręcznie. Jestem trochę zaniepokojony tym podejściem, szczególnie w odniesieniu do zestawów znaków i treści binarnych.
Jest równieżto pytanie, z jegoobecnie najwyżej punktowana odpowiedź sugerującMultipartPostHandler
moduł. Ale to nie różni się zbytnio od przepisów, o których wspomniałem, i dlatego moje obawy dotyczą również tego.
RFC 2388 Sekcja 4.3 jednoznacznie stwierdza, że zawartość ma być 7-bitowa, chyba że zostanie zadeklarowana inaczej, a zatemContent-Transfer-Encoding
nagłówek może być wymagane. Czy to oznacza, że musiałbym kodować pliki binarne Base64? Albo by to zrobiłContent-Transfer-Encoding: 8bit
wystarczy dla dowolnych plików? Czy powinien to przeczytać?Content-Transfer-Encoding: binary
?
Pola nagłówka ogólnie, afilename
w szczególności nagłówek, domyślnie są tylko ASCII. Chciałbym, aby moja metoda mogła również przekazywać nazwy plików innych niż ASCII. Wiem, że dla mojej obecnej aplikacji do przesyłania rzeczy na github prawdopodobnie nie będę tego potrzebował, ponieważ nazwa pliku jest podana w osobnym polu. Ale chciałbym, aby mój kod był wielokrotnego użytku, więc wolę kodować parametr nazwy pliku w zgodny sposób.RFC 2388 Sekcja 4.4 doradza format wprowadzony wRFC 2231, np.filename*=utf-8''t%C3%A4st.txt
.
Tak jakmultipart/form-data
jest zasadniczo typem MIME, pomyślałem, że powinno być możliwe użycieemail
pakiet ze standardowych bibliotek Pythona, aby skomponować mój post. Szczególnie skomplikowana obsługa pól nagłówków innych niż ASCII jest czymś, co chciałbym przekazać.
Napisałem więc następujący kod:
#!/usr/bin/python3.2
import email.charset
import email.generator
import email.header
import email.mime.application
import email.mime.multipart
import email.mime.text
import io
import sys
class FormData(email.mime.multipart.MIMEMultipart):
def __init__(self):
email.mime.multipart.MIMEMultipart.__init__(self, 'form-data')
def setText(self, name, value):
part = email.mime.text.MIMEText(value, _charset='utf-8')
part.add_header('Content-Disposition', 'form-data', name=name)
self.attach(part)
return part
def setFile(self, name, value, filename, mimetype=None):
part = email.mime.application.MIMEApplication(value)
part.add_header('Content-Disposition', 'form-data',
name=name, filename=filename)
if mimetype is not None:
part.set_type(mimetype)
self.attach(part)
return part
def http_body(self):
b = io.BytesIO()
gen = email.generator.BytesGenerator(b, False, 0)
gen.flatten(self, False, '\r\n')
b.write(b'\r\n')
b = b.getvalue()
pos = b.find(b'\r\n\r\n')
assert pos >= 0
return b[pos + 4:]
fd = FormData()
fd.setText('foo', 'bar')
fd.setText('täst', 'Täst')
fd.setFile('file', b'abcdef'*50, 'Täst.txt')
sys.stdout.buffer.write(fd.http_body())
Wynik wygląda tak:
--===============6469538197104697019==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name="foo"
YmFy
--===============6469538197104697019==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name*=utf-8''t%C3%A4st
VMOkc3Q=
--===============6469538197104697019==
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: form-data; name="file"; filename*=utf-8''T%C3%A4st.txt
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVm
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVm
YWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJjZGVmYWJj
ZGVmYWJjZGVmYWJjZGVm
--===============6469538197104697019==--
Wydaje się, że dobrze radzi sobie z nagłówkami. Zawartość pliku binarnego zostanie zakodowana w oparciu o base64, co może być możliwe do uniknięcia, ale powinno działać wystarczająco dobrze. Martwią mnie pola tekstowe między nimi. Są również zakodowane w oparciu o base64. Myślę, że zgodnie ze standardem powinno to działać wystarczająco dobrze, ale wolałbym mieć tam prosty tekst, na wypadek, gdyby jakiś głupi framework musiał radzić sobie z danymi na poziomie pośrednim i nie wie o danych zakodowanych w Base64.
pytaniaCzy mogę używać 8-bitowych danych dla moich pól tekstowych i nadal odpowiadam specyfikacji?Czy mogę uzyskać pakiet e-mail do serializacji moich pól tekstowych jako 8-bitowych danych bez dodatkowego kodowania?Jeśli muszę trzymać się kodowania 7-bitowego, czy mogę uzyskać implementację do cytowania dla tych części tekstu, w których kodowanie jest krótsze niż base64?Czy mogę również uniknąć kodowania base64 dla zawartości plików binarnych?Jeśli mogę tego uniknąć, powinienem napisaćContent-Transfer-Encoding
tak jak8bit
lub jakbinary
?Gdybym musiał sam serializować ciało, jak mógłbym użyćemail.header
pakiet samodzielnie formatować tylko wartości nagłówka? (email.utils.encode_rfc2231
robi to.)Czy jest jakaś implementacja, która już zrobiła wszystko, co próbuję zrobić?Pytania te są bardzo ściśle powiązane i można je podsumować jako„Jak byś to wdrożył”. W wielu przypadkach udzielenie odpowiedzi na jedno pytanie odpowiada lub zastępuje jedno. Mam więc nadzieję, że zgadzasz się, że jeden post dla wszystkich jest odpowiedni.