Formatieren Sie den mehrteiligen / Formulardatenkörper ordnungsgemäß

EinführungHintergrund

Ich schreibe ein Skript zum Hochladen von Dateien mit demmultipart/form-data Inhaltstyp definiert inRFC 2388. Langfristig versuche ich, ein einfaches Python-Skript bereitzustellenUploads von Binärpaketen für GithubHierbei werden formularähnliche Daten an Amazon S3 gesendet.

verbunden

Diese Frage hat bereits gefragt, wie dies zu tun ist, es ist jedoch noch keine akzeptierte Antwort vorhanden, unddesto nützlicher von den beiden Antworten hat es derzeit Punkte aufdiese Rezepte was wiederum die ganze Nachricht manuell erstellen. Ich bin etwas besorgt über diesen Ansatz, insbesondere in Bezug auf Zeichensätze und Binärinhalte.

Es gibt auchdiese Frage, mitAntwort mit der derzeit höchsten Punktzahl das vorschlagenMultipartPostHandler Modul. Aber das ist nicht viel anders als die Rezepte, die ich erwähnt habe, und deshalb gelten meine Bedenken auch dafür.

SorgenBinärer Inhalt

RFC 2388 Abschnitt 4.3 gibt ausdrücklich an, dass der Inhalt voraussichtlich 7 Bit sein wird, sofern nicht anders angegeben, und daher aContent-Transfer-Encoding Header Möglicherweise erforderlich. Bedeutet das, dass ich den Inhalt von Binärdateien mit Base64 codieren muss? Oder würdeContent-Transfer-Encoding: 8bit reicht das für beliebige dateien? Oder sollte das lesenContent-Transfer-Encoding: binary?

Zeichensatz für Header-Felder

Header-Felder im Allgemeinen und diefilename Insbesondere das Header-Feld ist standardmäßig nur ASCII-fähig. Ich möchte, dass meine Methode auch Nicht-ASCII-Dateinamen übergeben kann. Ich weiß, dass ich das für meine aktuelle Anwendung zum Hochladen von Dingen für Github wahrscheinlich nicht brauche, da der Dateiname in einem separaten Feld angegeben ist. Aber ich möchte, dass mein Code wiederverwendbar ist, also würde ich den Dateinamen-Parameter lieber auf eine konforme Art und Weise kodieren.RFC 2388 Abschnitt 4.4 weist auf das Format hin, das inRFC 2231, z.B.filename*=utf-8''t%C3%A4st.txt.

Mein AnsatzVerwenden von Python-Bibliotheken

Wiemultipart/form-data ist im Grunde ein MIME-Typ, ich dachte, dass es möglich sein sollte, die zu verwendenemail Paket aus den Standard-Python-Bibliotheken, um meinen Beitrag zu verfassen. Insbesondere den etwas komplizierten Umgang mit Nicht-ASCII-Header-Feldern möchte ich delegieren.

Arbeite so weit

Also habe ich folgenden Code geschrieben:

#!/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())

Das Ergebnis sieht so aus:

--===============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==--

Es scheint ziemlich gut mit Überschriften umzugehen. Der Inhalt von Binärdateien wird base64-codiert, was zwar vermeidbar ist, aber gut genug funktionieren sollte. Was mich beunruhigt, sind die Textfelder dazwischen. Sie sind ebenfalls base64-codiert. Ich denke, dass dies nach dem Standard gut genug funktionieren sollte, aber ich würde lieber reinen Text darin haben, nur für den Fall, dass ein dummes Framework auf einer mittleren Ebene mit den Daten umgehen muss und nichts über Base64-codierte Daten weiß.

FragenKann ich 8-Bit-Daten für meine Textfelder verwenden und trotzdem der Spezifikation entsprechen?Kann ich mit dem E-Mail-Paket meine Textfelder als 8-Bit-Daten ohne zusätzliche Codierung serialisieren?Wenn ich mich an eine 7-Bit-Kodierung halten muss, kann ich dann die Implementierung veranlassen, für die Textteile, bei denen diese Kodierung kürzer als base64 ist, die Druckversion mit Anführungszeichen zu verwenden?Kann ich die Base64-Codierung auch für Binärdateiinhalte vermeiden?Wenn ich es vermeiden kann, soll ich das schreibenContent-Transfer-Encoding wie8bit oder alsbinary?Wenn ich den Körper selbst serialisieren müsste, wie könnte ich den verwenden?email.header Paket allein, um nur Header-Werte zu formatieren? (email.utils.encode_rfc2231 macht dies.)Gibt es eine Implementierung, die bereits alles getan hat, was ich zu tun versuche?

Diese Fragen sind sehr eng miteinander verbunden und können wie folgt zusammengefasst werden"Wie würden Sie dies umsetzen?". In vielen Fällen wird durch die Beantwortung einer Frage eine andere Frage beantwortet oder obsolet. Ich hoffe, Sie stimmen zu, dass ein einzelner Beitrag für alle von ihnen angemessen ist.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage