With bei HTTP File Upload

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
chino
User
Beiträge: 2
Registriert: Sonntag 29. Dezember 2019, 21:21

Hallo Leute,

ich hoffe ihr könnt mir weiterhelfen, da ich mir derzeit an einer Fragestellung den Kopf zerbreche. Ich lade per HTTP-requests Dateien über eine REST-API hoch. Funktionierte soweit problemlos. Leider wird bei meinem derzeit funktionierenden Konstrukt die hoch geladene Datei nach dem Upload nicht wieder geschlossen/freigegeben, so dass ich sie nicht aus dem Windows-Verzeichnis löschen kann. Also wollte ich meinen Code umschreiben und per WITH das Schließen der Datei, nach dem Upload, realisieren. Funktioniert nur leider nicht so, wie ich es mir vorgestellt hatte. Vielleicht wisst ihr Rat?

Hier mein derzeit funktionierender Code:

Code: Alles auswählen

        for root, dirs, files in os.walk('%sworkfolder/%s/%s' % (start_path, pid, str(row[0]))):
            for name in files:
                url = "http://%s/system/%s/sec?pid=%s" % (server_name, pid, str(row[0]))

                headers = {
                    'Accept': "*/*",
                    'Cache-Control': "no-cache",
                    'Accept-Encoding': "gzip, deflate",
                    'Cookie': session_cookie,
                    'Connection': "keep-alive",
                    'cache-control': "no-cache"
                    }
                
                files={'files': open('%s/%s' % (root, name),'rb')}
                response = requests.request("POST", url, files=files, headers=headers)
Hier mein neuer, bis dato leider erfolgloser, Wurf.

Code: Alles auswählen

        for root, dirs, files in os.walk('%sworkfolder/%s/%s' % (start_path, pid, str(row[0]))):
            for name in files:
                url = "http://%s/system/%s/sec?pid=%s" % (server_name, pid, str(row[0]))

                headers = {
                    'Accept': "*/*",
                    'Cache-Control': "no-cache",
                    'Accept-Encoding': "gzip, deflate",
                    'Cookie': session_cookie,
                    'Connection': "keep-alive",
                    'cache-control': "no-cache"
                    }
                
                with open('%s/%s' % (root, name), 'rb') as files: response = requests.request("POST", url, files=files, headers=headers)
Folgender Fehler wird ausgegeben:

Code: Alles auswählen

ValueError: too many values to unpack (expected 2)
Ich bin dankbar für jeglichen Rat.

Schönen Abend!
Benutzeravatar
__blackjack__
User
Beiträge: 14051
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@chino: An der Namensgebung `files` sollte vielleicht schon auffallen das da etwas falsch ist, denn `files` ist bei Deinem zweiten Versuch ja nur *ein* Dateiobjekt. Im ersten Code übergibst Du da aber kein Dateiobjekt sondern ein Wörterbuch. Es ist auch nicht so eine wirklich gute Idee `files` innerhalb von zwei verschachtelten Schleifen für zwei völlig unterschiedliche Werte zu verwenden.

Das ”äussere” `files` ist auch eher `filenames` und das ”innere” `files` ist einfach nur `file`, denn es ist wie gesagt nur eine einzelne Datei.

``%`` für Zeichenkettenformatierung würde ich nicht mehr verwenden. Es gibt ja nun schon länger die `format()`-Methode auf Zeichenketten. *Aaaber*: für das zusammensetzen von Pfadteilen ist das sowieso falsch, weil man Pfadteile in vielen Fällen nicht einfach wie normale Zeichenketten behandeln kann. Zusammensetzen geht beispielsweise mit `os.path.join()` sicher und korrekt. Wobei man bei aktuellem Code wohl eher auf das `pathlib`-Modul zurückgreifen würde.

Bei aktuellem Python (≥3.6) gibt es f-Zeichenkettenliterale um Werte in Zeichenketten zu formatieren.

Bei ``str(row[0])`` wäre das `str()` überflüssig denn diese Umwandlung passiert ja schon beim Formatieren des Wertes in die Zeichenkette. Was man aber bei Pfaden wie gesagt nicht so macht. Und bei der URL würde ich das auch `requests` überlassen die Parameter korrekt zu kodieren.

Gibt es einen Grund warum Du nicht `requests.post()` verwendest?

`headers` und das `params`-Argument von der Anfrage ändern sich innerhalb der Schleife nicht, können also *einmal* *vor* der Schleife definiert werden.

Ungetesteter Zwischenstand:

Code: Alles auswählen

        ...
        some_other_pid = row[0]  # TODO Use a meaningful name here.
        params = {"pid": str(some_other_pid)}
        headers = {
            "Accept": "*/*",
            "Cache-Control": "no-cache",
            "Accept-Encoding": "gzip, deflate",
            "Cookie": session_cookie,
            "Connection": "keep-alive",
            "cache-control": "no-cache",
        }
        #
        # TODO Replace by code using `pathlib`.
        #
        for base_path, _, filenames in os.walk(
            os.path.join(start_path, "workfolder", pid, str(some_other_pid))
        ):
            for filename in filenames:
                with open(os.path.join(base_path, filename), "rb") as file:
                    response = requests.post(
                        f"http://{server_name}/system/{pid}/sec",
                        params=params,
                        files={"files": file},
                        headers=headers,
                    )
                response.raise_for_status()
Edit: Mit `pathlib`:

Code: Alles auswählen

        ...
        some_other_pid = row[0]  # TODO Use a meaningful name here.
        params = {"pid": str(some_other_pid)}
        headers = {
            "Accept": "*/*",
            "Cache-Control": "no-cache",
            "Accept-Encoding": "gzip, deflate",
            "Cookie": session_cookie,
            "Connection": "keep-alive",
            "cache-control": "no-cache",
        }
        base_path = start_path / "workfolder" / str(pid) / str(some_other_pid)
        for path in base_path.rglob("*"):
            if path.is_file():
                with path.open("rb") as file:
                    response = requests.post(
                        f"http://{server_name}/system/{pid}/sec",
                        params=params,
                        files={"files": file},
                        headers=headers,
                    )
                response.raise_for_status()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
chino
User
Beiträge: 2
Registriert: Sonntag 29. Dezember 2019, 21:21

Hi blackjack,

vielen Dank für deine Antwort, du hast mir wirklich sehr geholfen. Vielen Dank auch für die weiteren Erläuterungen und Informationen. Ich habe mein Script überarbeitet und es funktioniert. Das ich auf "requests.post()" verzichtet habe, war gar nicht beabsichtigt, ich habe es schlichtweg so gelernt. Bei den Bezeichnungen in meinem Script (z.B. files) werde ich zukünftig auch sauberer arbeiten, das ist mit dem Lernprojekt gewachsen und ausgeartet. Schönen Abend!
Antworten