Seite 1 von 2
Download von Dateien
Verfasst: Sonntag 5. September 2021, 08:34
von Sternenregen
Sobald der User auf der HTML Seite auf einen Button klickt, soll ein csv gedownloadet werden können. Das CSV wird aus einem Dataframe erstellt.
Ich habe bereits ein minimal Exempel auf meinem privaten Rechner erstellt und es funktioniert.
Mache ich haargenau das gleiche auf dem Arbeitsrechner, wird ein "RecursionsError" geworfen und dass ich die maximale Anzahl an Rekursiven erreicht habe.
Der einzige Unterschied ist, dass natürlich in dem Datensatz auf dem Arbeitsrechner wesentlich mehr Daten sind.
Ich bin mir zudem unschlüssig, wo genau das Problem liegt.
Ich habe auch versucht das Format der Datei zu ändern. (HTML und JSON), aber dann passiert irgendwie
überhaupt nichts. Kein Fehler, nichts. Aber der Download wird nicht gestartet.
Hier ist mein Code:
Code: Alles auswählen
@application.route(/....)
def download_file():
buff_bytes_csv = download_result()
return send_file(buffered_bytes_csv, mimetype="text/csv", as_attachment=True, attachment_filename="result.csv")
Code: Alles auswählen
def download_result():
....
csv_file = df.to_csv()
buffered_str = io.StringIO(csv_file)
buffered_bytes = io.BytesIO(buffered_str.read().enconde("utf-8"))
return buffered_bytes
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 09:01
von nezzcarth
Bitte zeig doch den gesamten Traceback, den du bekommst. Ohne kann man das kaum beantworten.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 09:48
von Sternenregen
Ich wollte den Fehler gerade reproduzieren und nun taucht er nicht mehr auf -.- Es passiert aber trotzdem kein Download.
Anhand meiner Google Recherche kann ich dir aber den relevanten Tracebackteil aus meiner Googlesuche zeigen:
Entweder das hilft weiter, oder ich muss nach Alternativen schauen bzw gucken ob der Fehler erneut auftritt.
Ich möchte aber noch erwähnen, dass die Anwendung in einem Dockercontainer läuft. Vielleicht hilft diese Information auch weiter.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 09:59
von Sirius3
Das ist eben nicht der relevante Teil des Tracebacks. In dem von Dir gezeigten Codefragment kommt keine Rekursion vor, dort liegt also nicht der Fehler. Mit Traceback sind die vielen Zeilen gemeint, in denen die Zeilen stehen,in denen der Fehler auftaucht und dazu brauchen wir auch den relevanten Code. Sonst können wir das Problem nicht verstehen und auch nicht helfen.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 11:04
von rogerb
Selbst dieses Codefragment enthält schon zwei Fehler, die unabhängig vom geschilderten Problem auftreten.
mal heißt es "buff_bytes_csv ", dann soll aber "buffered_bytes_csv" gesendet werden.
enconde("utf-8") ist falsch. Richtig wäre: encode("utf-8")
Aber das ist wohl nicht aufgefallen, weil es vorher schon in die Hose geht.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 12:46
von __blackjack__
Der Umweg über `StringIO` ist auch irgendwie total überflüssig.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 15:08
von Sternenregen
rogerb hat geschrieben: Sonntag 5. September 2021, 11:04
Selbst dieses Codefragment enthält schon zwei Fehler, die unabhängig vom geschilderten Problem auftreten.
mal heißt es "buff_bytes_csv ", dann soll aber "buffered_bytes_csv" gesendet werden.
enconde("utf-8") ist falsch. Richtig wäre: encode("utf-8")
Aber das ist wohl nicht aufgefallen, weil es vorher schon in die Hose geht.
Das hast du super erkannt, Sherlock.
__blackjack__ hat geschrieben: Sonntag 5. September 2021, 12:46
Der Umweg über `StringIO` ist auch irgendwie total überflüssig.
Wenn du dir schon die Mühe machst zu Antworten, dann begründe es doch auch. Dann kann ich vielleicht noch was lernen. Ansonsten ist eigentlich nur dein Post überflüssig.
Wie gesagt. ich versuche den Fehler zu reproduzieren.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 16:10
von sparrow
@Sternemregen: Umgekehrt wird ein Schuh draus. StringIO an der Stelle zu verwenden macht überhaupt gar keinen Sinn. Was erhoffst du dir davon? Welchen Vorteil siehst du darin, die Zeichenkette in das File-Like-Object zu stecken, dann wieder herauszuholen und dann encode darauf aufzurufen?
Ich sehe übrigens nicht, warum es hier einen Grund für dich gibt patzig zu werden. Wenn du hier fehlerhaften Code einstellst, dann musst du damit rechnen, dass dir Menschen sagten,d ass er fehlerhaft ist. Du hättest ja auch einfach den richtigen, lauffähigen Code nehmen sollen, statt dir offensichtlich fehlerhaften auszudenken.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 17:07
von __blackjack__
@Sternenregen: Ich dachte das wäre offensichtlich. Da wird eine Zeichenkette in ein `StringIO`-Objekt geschrieben, und das wird dann wieder ausgelesen. Was zu der gleichen Zeichenkette führt die man da rein geschrieben hat. Das wäre als würde man bei ganzen Zahlen ``b = a * 1`` oder ``b = a + 0`` schreiben. Kann man machen, ist aber nicht sinnvoll, weil es nichts am Wert von `a` ändert, und man den auch gleich zum weiterrechnen verwenden könnte. ``b = io.StringIO(a).read()`` ist letztlich das gleiche. Danach gilt immer ``b == a``. Man könnte auch ``b = a`` schreiben, und da stellt sich dann die Frage wofür man `b` braucht.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 19:03
von noisefloor
Hallo,
Ich habe bereits ein minimal Exempel auf meinem privaten Rechner erstellt und es funktioniert.
Ich orakle mal - bedingt durch fehlenden Code - dass genau das der Ausgangspunkt ist. Python wirft den Rekursionsfehler, wenn die Rekursionstiefe 1000 (sofern du den Wert nicht geändert hast) überschreitet. Da kommst du mit wenigen Testdaten wahrscheinlich nicht hin, mit realen Datenmengen dann vielleicht schon?
Generell ist Rekursion _nicht_ das Mittel der Wahl in Python, weil Python null Optimierung für Rekursion hat. Und wenn du, wie der Ausgangspost vermuten lässt, Pandas benutzt, dann ist der Einsatz von Rekursion extrem unüblich.
Gruß, noisefloor
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 20:32
von Sternenregen
Guten Abend noisefloor,
vielen Dank für deinen Post. In der tat nutze ich Pandas um ein CSV-File zu erstellen. Dieses möchte ich dann downloaden können. Den Rekursionsfehler erhalte ich mittlerweile nicht mehr. Warum kann ich leider nicht beantworten.
UPDATE:
Mittlerweile bin ich in meinen Recherchen weiter gekommen. Ich habe jetzt auch mein Minimal-Beispiel in ein Docker Container gepackt. Es funktioniert alles einwandfrei. Der Code funktioniert. Der einzige Unterschied zu der Produktivumgebung ist die Datenmenge.
Möchte ich der tatsächlichen Anwendung etwas downloaden, passiert nichts. Weder im Browser, noch in den Docker-Logs noch sonst irgendwo ist nur der Hauch eines Fehlers zu erkennen. Es passiert einfach nichts. Kein File oder sonst irgendetwas was auf mit einem Download zu tun hätte. Ich habe auch mittlerweile viele Sachen probiert die ich im Netz gefunden habe, bisher leider ohne Erfolg. Eventuell ändere ich mal das Format des Files.
Re: Download von Dateien
Verfasst: Sonntag 5. September 2021, 20:48
von __blackjack__
@Sternenregen: Vielleicht dauert es einfach nur sehr lange und/oder der Speicher reicht nicht aus. Das Vorgehen ist nicht wirklich speicherschonend. Alleine der Code den wir sehen hat das ganze Ergebnis als DataFrame und als Zeichenkette und als Bytes-Objekt gleichzeitig im Speicher. Und einen grossen DataFrame dynamisch aufzubauen ist auch nicht so toll, und wenn da auch alles an Namen gebunden wird, gibt es da das komplette Ergebnis vielleicht *noch mal* komplett im Speicher.
Ich würde mir mal den Speicherverbrauch auf dem Produktivsystem anschauen.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 07:15
von Sternenregen
__blackjack__ hat geschrieben: Sonntag 5. September 2021, 20:48
@Sternenregen: Vielleicht dauert es einfach nur sehr lange und/oder der Speicher reicht nicht aus. Das Vorgehen ist nicht wirklich speicherschonend. Alleine der Code den wir sehen hat das ganze Ergebnis als DataFrame und als Zeichenkette und als Bytes-Objekt gleichzeitig im Speicher. Und einen grossen DataFrame dynamisch aufzubauen ist auch nicht so toll, und wenn da auch alles an Namen gebunden wird, gibt es da das komplette Ergebnis vielleicht *noch mal* komplett im Speicher.
Ich würde mir mal den Speicherverbrauch auf dem Produktivsystem anschauen.
Vielen Dank für den Hinweis. Dem werde ich auf jeden Fall gleich mal auf dem Grund gehen.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 07:50
von Sternenregen
So, das erste Test Exampel war auf einem Mac. Das zweite Test Exampel habe ich jetzt auf einer Windows Maschine erstellt.
Gleicher Code. Aber jetzt wird tatsächlich eine Fehlermeldung geworfen:
Code: Alles auswählen
File "C:\Users\user\PycharmProjects\ex2\venv\lib\site-packages\flask\app.py", line 1513, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\user\PycharmProjects\ex2\venv\lib\site-packages\flask\app.py", line 1499, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\user\PycharmProjects\ex2\main.py", line 143, in save_cluster_result
return send_file(csv_file,
File "C:\Users\user\PycharmProjects\ex2\venv\lib\site-packages\flask\helpers.py", line 612, in send_file
return werkzeug.utils.send_file(
File "C:\Users\user\PycharmProjects\ex2\venv\lib\site-packages\werkzeug\utils.py", line 697, in send_file
stat = os.stat(path)
OSError: [WinError 123] Die Syntax für den Dateinamen, Verzeichnisnamen oder die Datenträgerbezeichnung ist falsch: 'C:\\Users\\user\\PycharmProjects\\ex2\\,0\r\ntest1,123\r\ntest2,123\r\n
Ich schätze mal die Fehlermeldung beruht auf der SPeicheradresse des Dataframes. Denn ich habe in dem Test Exampel keinerlei Pfadangaben. Das Dataframe wird on the fly erzeugt. Generiert wird dieses aus einem Dictionary mit 2 key/Value Einträgen.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 07:56
von noisefloor
Hallo,
nee, da ist ein anderer, grundlegender Fehler drin. So wie die letzte Zeile des Traceback aussieht versuchst du, die Daten der CSV-Datei als Dateinamen zu schreiben. Was natürlich nicht geht, weil da Zeilenumbrüche (`\r\n`) drin sind.
Also muss in deinem Progammcode ein Fehler sein.
Gruß, noisefloor
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 08:07
von Sirius3
Du hast offensichtlich den Inhalt Deines Dataframes als Pfad benutzt. Ohne Code läßt sich dazu nichts sagen, aber wenn ich raten müßte, hast Du nicht nur StringIO sondern auch BytesIO entfernt, was dann eins zu viel war. send_file erwartet als erstes Argument einen Dateinamen oder eben ein file-like Objekt.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 08:24
von Sternenregen
Das ist der Code.
Code: Alles auswählen
@app.route('/saveResults', methods=['GET', 'POST'])
def save_results():
dicttest = {"test1": 123, "test2": 123}
df = pd.DataFrame.from_dict(dicttest, orient='index')
csvfile = df.to_csv()
buffered_str = io.StringIO(csvfile)
bufferd_bytes = io.BytesIO(buffered_str.read().encode("utf-8"))
return send_file(bufferd_bytes,
mimetype="text/csv",
download_name='test.csv',
as_attachment=True)
Code: Alles auswählen
$(document).ready(function(){
$('#save-result').click(function(){
console.log("asdf");
$.ajax({
type: 'POST',
url: '/saveResults',
success: function(data){
console.log("successfasdf");
}
});
});
});
Es wird sogar in den Success gesprungen. Aber nichts passiert. Nichts wo gedownloaded wird. Aber ja, ich stimme dir zu. Das ist ein grundlegendes Problem.
Der Mac kriegt es jedenfalls hin.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 08:42
von Sirius3
Das ist wieder ein anderer Code, als der, der die Fehlermeldung produziert hat.
Dort stand nämlich
Korrekt wäre:
Code: Alles auswählen
@app.route('/saveResults', methods=['GET', 'POST'])
def save_results():
dicttest = {"test1": 123, "test2": 123}
df = pd.DataFrame.from_dict(dicttest, orient='index')
buffered_bytes = io.BytesIO()
df.to_csv(buffered_bytes)
return send_file(buffered_bytes,
mimetype="text/csv",
download_name='test.csv',
as_attachment=True)
Und was erwartest Du? Natürlich kommt da kein Download. Du machst einen AJAX-Call, dessen Ergebnis von Javascript im Browser verarbeitet wird. Du machst aber mit `data` nichts.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 09:49
von Sternenregen
Vielen Dank. Darin lag der Denkfehler. Ein dummer Fehler.
Vielen Dank an alle Beteiligten.
Re: Download von Dateien
Verfasst: Montag 6. September 2021, 11:40
von Sternenregen
__blackjack__ hat geschrieben: Sonntag 5. September 2021, 20:48
@Sternenregen: Vielleicht dauert es einfach nur sehr lange und/oder der Speicher reicht nicht aus. Das Vorgehen ist nicht wirklich speicherschonend. Alleine der Code den wir sehen hat das ganze Ergebnis als DataFrame und als Zeichenkette und als Bytes-Objekt gleichzeitig im Speicher. Und einen grossen DataFrame dynamisch aufzubauen ist auch nicht so toll, und wenn da auch alles an Namen gebunden wird, gibt es da das komplette Ergebnis vielleicht *noch mal* komplett im Speicher.
Ich würde mir mal den Speicherverbrauch auf dem Produktivsystem anschauen.
Ok. Was könnte man da dagegen tun?
Jetzt wo es vermeintlich funktioniert, bekomme ich wieder den Rekursionsfehler: Aber jetzt kann ich ihn auch posten:
Code: Alles auswählen
ERROR:app:Exception on /downloadResult [GET]
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python3.9/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/meineApp/app.py", line 239, in get_result
buffered_csv = get_result()
File "/meineApp/app.py", line 239, in get_result
buffered_csv = get_result()
File "/meineApp/app.py", line 239, in get_result
buffered_csv = get_result()
[Previous line repeated 985 more times]
RecursionError: maximum recursion depth exceeded
Wie könnte man das ganze effizienter gestalten?