Verarbeiten einer Datei mit Python3 und Bottle

Django, Flask, Bottle, WSGI, CGI…
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 13:01

Hallo, ich arbeite gerade an einem Projekt für die Uni. Wir haben die Grundlagen von Python3 gelernt und müssen nun ein kleines Programm schreiben, welches eine fastq datei einliest und diese analysiert. Ich dachte mir, dass es interessant wäre ein html-basierendes Userinterface zu erstellen. So bin ich auf Bottle gestoßen. Ich möchte nun, dass der Benutzer eine Datei "hochlädt" (erstmal ohne einen richtigen Server) und diese von einem Pythonprogramm weiterverarbeitet wird. Leider scheitere ich an der Stelle mit der Weitergabe der Datei. Ich weiß nicht, wie ich auf die Datei zugreifen kann, denn diese erscheint nicht im Verzeichnis von dem "serverseitigen" Bottleprogramm. Dennoch meldet mir der Server, dass der upload erfolgreich war und bei einem erneuten Upload der gleichen Datei, dass diese bereits vorhanden ist (Diese Meldung ist etwas nervig, es gibt doch die Klasse UploadFile mit der Funktion save, die ein Überschreiben ermöglicht, wie kann ich diese einbinden?)

Folgendes habe ich mir durchgelesen, aber anscheinend übersehe ich etwas:
http://bottlepy.org/docs/dev/tutorial.html#file-uploads
http://bottlepy.org/docs/dev/api.html#bottle.FileUpload.file
http://stackoverflow.com/questions/12361581/how-do-i-access-an-uploaded-file-with-bottle
http://stackoverflow.com/questions/14296438/bottle-file-upload-and-process
und noch einige weitere Webseiten.

bottle_test.py:
  1. from bottle import route, run, template, static_file, request, response, url, default_app, get, post, FileUpload
  2. import bottle
  3. import os
  4. # Aufrufen der Hauptseite
  5. @route('/')
  6. def index():
  7.     return template('main_template')
  8.    
  9. # Einbinden unterschiedlicher Dateien z.B. Bilder oder CSS-Files
  10. @route('/static/style/<filepath:re:.*\.css>')
  11. def server_static(filepath):
  12.     return static_file(filepath, root='static/style')
  13.  
  14. @route('/static/images/<filepath:re:.*\.(jpg|png|gif|ico|svg)>')
  15. def img(filepath):
  16.     return static_file(filepath, root="static/images")
  17.  
  18. @route('/static/sonstige-bilder/<filepath:re:.*\.(jpg|png|gif|ico|svg)>')
  19. def img(filepath):
  20.     return static_file(filepath, root='static/sonstige-bilder')
  21. # Formularabfrage
  22. @route('/repeat', method='POST')
  23. def do_login():
  24.     username = request.forms.get('username')
  25.     password = request.forms.get('password')
  26.     if username == 'arsenij' and password == '1234':
  27.         return "<p>Your login information was correct.</p>"
  28.     else:
  29.         return "<p>Login failed.</p>"
  30.  
  31. @route('/upload', method='POST')
  32. def do_upload():
  33.     category = request.forms.get('category')
  34.     upload = request.files.get('upload')
  35.     name, ext = os.path.splitext(upload.filename)
  36.     if ext not in ('.fastq'):
  37.         return 'File extension not allowed.'
  38.  
  39.     save_path = '/tmp/(category)'
  40.     if not os.path.exists(save_path):
  41.         os.makedirs(save_path)
  42.  
  43.     file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
  44.     upload.save(file_path)
  45.     return 'File uploaded'
  46.    
  47. if __name__ == '__main__':
  48.     bottle.debug(True)
  49.     bottle.run(host='0.0.0.0', port=8080,  reloader=True)
  50.  


main_template.tpl
  1. ...
  2. <form action="/upload" method="post" enctype="multipart/form-data">
  3. Category:      <input type="text" name="category" />
  4. Select a file: <input type="file" name="upload" />
  5. <input type="submit" value="Start upload" />
  6. </form>
  7. ...
Zuletzt geändert von BlackJack am Freitag 3. Februar 2017, 23:24, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 5741
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Sirius3 » Freitag 3. Februar 2017, 14:15

@Mars_ars: wo vermutest Du, dass die Datei landet und wo schaust Du danach? Bei save kann man overwrite=True angeben, sodass keine nervige Meldunge mehr kommen sollte. Normalerweise hat man nur eine Route zu den statischen Dateien und nicht eine pro Dateityp.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 14:46

also ich stelle mir das folgendermaßen vor:

fastqdatei irgendwo auf der Platte

Verzeichnis in dem Bottle liegt: (Dateien groß geschrieben)
.
|-BOTTLE.PY
|-BOTTLE_TEST.PY
|-__pycache__
|-views
| |-MAIN_TEMPLATE.TPL
|-static
| |-style
| | |-CSS_DATEIEN
| |-sonstige-bilder
| | |-BILD_DATEIEN
| |-images
| | |-BILD_DATEIEN
|-tmp
| |-UPLOADED_FILE.fastq

ich würde erwarten, dass
  1. save_path = '/tmp/(category)'
  2.     if not os.path.exists(save_path):
  3.         os.makedirs(save_path)
  4.  
  5.     file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
  6.     upload.save(file_path, overwrite=True)
ein neues Verzeichnis anlegt, indem die Datei liegt und ich mit einer nächsten Funktion in bottle_test.py ganz normal die datei öffnet und mit dem Inhalt weiterarbeitet.
Zuletzt geändert von BlackJack am Freitag 3. Februar 2017, 23:25, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 15:02

Habe gerade die Datei bottle_test.py erweitert und eine neue Teplate_Datei erstellt. Ich weiß schon, dass für jede neue Antwort oder Änderung der Seite nicht unbedingt eine neue Template-Datei erstellt werden muss. Man könnte das Modular viel hübscher lösen. Aber übersichtshalber und zu Lernzwecken wird es reichen. :wink:

Jetzt wird der Benutzer nach dem Upload auf die neue Template weitergeleitet, wo ein weiterer Butten ist, der nun die Verarbeitung der Datei ermöglichen sollte. Nur leider meldet er mir, dass das Verzeichnis nicht existiert:

bottle_test.py:

  1. @route('/upload', method='POST')
  2. def do_upload():
  3.     category = request.forms.get('category')
  4.     upload = request.files.get('upload')
  5.     name, ext = os.path.splitext(upload.filename)
  6.     if ext not in ('.fastq'):
  7.         return 'File extension not allowed.'
  8.  
  9.     save_path = '/tmp/(category)'
  10.     if not os.path.exists(save_path):
  11.         os.makedirs(save_path)
  12.  
  13.     file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
  14.     upload.save(file_path, overwrite=True)
  15.     return template('upload')
  16.  
  17. @route('/analyse', method='POST')
  18. def analyse():
  19.     proceed = request.forms.get('analyse')
  20.     fileobj = open('upload.filename', 'r')
  21.     for line in fileobj:
  22.         print(line)


  1. ...
  2. <p>File successful uploaded</p>
  3.     <p>want you see your file with the coding was used?</p>
  4.     <p><form action="/analyse" method="post">
  5.     <p><input value="rec_analyse" type="submit" name="analyse" /></p>
  6. </form>
  7. ...
Zuletzt geändert von BlackJack am Freitag 3. Februar 2017, 23:27, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 15:04

Übrigens vielen Dank für die Antwort!
Sirius3
User
Beiträge: 5741
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Sirius3 » Freitag 3. Februar 2017, 15:29

@Mars_ars: versuch mal genau nachzuvollziehen, wohin die Datei mit welchem Namen geschrieben wird, und welche Datei Du bei analyse versuchst zu öffnen.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 16:33

Hm...
Also unter dem Verzeichnis /tmp/ wird ein neuer Ordner erstellt mit dem benutzerdefinierten Namen (category)
der richtige Name der Datei wird in der Variable upload gespeichert
der Befehl die Datei zu speichern beinhaltet die Variable file_path, die so aussieht: /tmp/(category)/upload.filename
ich muss noch die Variablen in die neue Funktion importieren und er zeigt mir wieder eine Fehlermeldung. Außerdem müsste man doch die Verzeichnisse sehen können.

  1. @route('/analyse', method='POST')
  2. def analyse():
  3.     upload = do_upload(upload)
  4.     file_path = do_upload(file_path)
  5.     proceed = request.forms.get('analyse')
  6.     fileobj = open(file_path, 'r')
  7.     for line in fileobj:
  8.         print(line)


Leicht modifiziert aber immer noch nicht richtig.
Bin ich mit meinen Überlegungen auf dem Holzweg? Wo ist der Denkfehler?
Zuletzt geändert von BlackJack am Freitag 3. Februar 2017, 23:29, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Freitag 3. Februar 2017, 17:45

Wenn ich in der Funktion /upload bei return mehrere Variablen übergebe, kommt die folgende Fehlermeldung:

Code: Alles auswählen

return template('upload'), file_path, upload


Critical error while processing request: /upload
Error:

TypeError('sequence item 2: expected str instance, FileUpload found',)

Traceback:

Traceback (most recent call last):
File "/home/arsenij/Dokumente/Final_project/test/bottle.py", line 954, in wsgi
out = self._cast(self._handle(environ))
File "/home/arsenij/Dokumente/Final_project/test/bottle.py", line 894, in _cast
out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
TypeError: sequence item 2: expected str instance, FileUpload found

Darf ich dann nicht mehrere Variablen übergeben oder mache ich nur etwas grundlegend falsch.
Benutzeravatar
BlackJack
Moderator
Beiträge: 31927
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon BlackJack » Freitag 3. Februar 2017, 18:39

@Mars_ars: Der Rückgabwert muss eine Sequenz von Zeichenketten sein wenn man eine Sequenz zurück gibt. Und das dritte Element ist vom Typ `FileUpload` und nicht vom Typ `str`.
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 5741
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Sirius3 » Freitag 3. Februar 2017, 19:47

@Mars_ars: hast Du jetzt in das Verzeichnis auf /tmp/ geschaut und dort die Dateien gefunden? Woher glaubst Du kommt in Deiner neuen analyse-Funktion upload und file_path? Und was glaubst Du macht damit ein do_upload? Wenn Du file_path in der analyse-Methode haben willst, mußt Du in do_upload file_path in Dein Template als <input>-Parameter einbauen, was aber an sich eine schlechte Idee ist, weil jeder der einen Analyse-Request absetzt, damit beliebige Dateien angeben könnte. Sinnvoller wäre es, einen zufälligen Schlüssel zu erzeugen und die hochgeladene Datei damit zu benennen. Die Gefahr, dass damit Unheil anrichten kann, ist viel geringer.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Samstag 4. Februar 2017, 18:50

@Sirius3 Vielen herzlichen Dank für deine Antworten und die Unterstützung. Ich habe endlich das Verzeichnis gefunden. Erwartet habe ich es ja in dem Verzeichnis von bottle_test.py aber so macht es natürlich auch Sinn :D
Die Sicherheitsfrage ist mir nicht ganz deutlich geworden, da ja der Upload und die Analyse unabhängig voneinander sind. Ich habe nun ein paar kleine Verbesserungen eingefügt: Entfernen des Inputs für die Kategorie/Verzeichnis; Generieren eines zufälligen Schlüssels und umbenennen der upload.filename. Leider scheitere ich an der Variablenübergabe von der Funktion

Code: Alles auswählen

do_upload
zu

Code: Alles auswählen

analyse
. Ich bin mir auch nicht sicher, ob dein Sicherheitshinweis dadurch erfüllt wurde.

Code: Alles auswählen

@route('/upload', method='POST')
def do_upload():
    new_key=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
    upload = request.files.get('upload')
    name, ext = os.path.splitext(upload.filename)
    if ext not in ('.fastq'):
        return 'File extension not allowed.'

    save_path = '/tmp/fastq_prog'
    if not os.path.exists(save_path):
        os.makedirs(save_path)

    file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
    upload.save(file_path, overwrite=True)

    new_name = '/tmp/fastq_prog/'+new_key
    old_name = '/tmp/fastq_prog/'+upload.filename
    os.rename(old_name, new_name)

    return template('upload'), new_key

@route('/analyse', method='POST')
def analyse():
    new_key1 = do_upload(new_key)

    filename = open('/tmp/fastq_prog/'+new_key)
    for line in filename:
        print(line)


Code: Alles auswählen

<p>File successful uploaded</p>
         <p>want to see your file with the coding was used?</p>
         <p><form action="/analyse" method="post" enctype="multipart/form-data">
            <p><input value="rec_analyse" type="submit" name="analyse" /></p>
         </form>


So geht es leider auch nicht: :K

Code: Alles auswählen

@route('/upload', method='POST')
def do_upload():
    new_key=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
    upload = request.files.get('upload')
    name, ext = os.path.splitext(upload.filename)
    if ext not in ('.fastq'):
        return 'File extension not allowed.'

    save_path = '/tmp/fastq_prog'
    if not os.path.exists(save_path):
        os.makedirs(save_path)

    file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
    upload.save(file_path, overwrite=True)

    new_name = '/tmp/fastq_prog/'+new_key
    old_name = '/tmp/fastq_prog/'+upload.filename
    os.rename(old_name, new_name)

    return template('upload')

@route('/analyse', method='POST')
def analyse():
    new_key = request.files.get('new_key')

    filename = open('/tmp/fastq_prog/'+new_key)
    for line in filename:
        print(line)

Code: Alles auswählen

<p>File successful uploaded</p>
         <p>want to see your file with the coding was used?</p>
         <p><form action="/analyse" method="post" enctype="multipart/form-data">
            <p><input value={{new_key}} type="submit" name="analyse" /></p>
         </form>
Sirius3
User
Beiträge: 5741
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Sirius3 » Samstag 4. Februar 2017, 19:36

@Mars_ars: genau, do_upload ist unabhängig von analyse. Bei Funktionen werden vom Nutzer über einen Web-Browser angestoßen. Der einzige Weg, Informationen zu übertragen, ist über das Template mit hidden-<input>-Tags. Dein erster Versuch ist in der Hinsicht noch weit von der Lösung entfernt, der zweite schon fast richtig, nur dass es eben keine files in analyse gibt, sondern nur normale POST-Argumente. Pfade setzt man weder mit .format noch mit + zusammen, sondern mit os.path.join.
Mars_ars
User
Beiträge: 16
Registriert: Freitag 3. Februar 2017, 12:09

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Mars_ars » Samstag 4. Februar 2017, 21:55

Nach ewig langer Suche nach dem Fehler bin ich an der sensiblen Grenze der Verzweiflung angekommen :oops: :
  1. @route('/upload', method='POST')
  2. def do_upload():
  3.     new_key=''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8))
  4.     upload = request.files.get('upload')
  5.     name, ext = os.path.splitext(upload.filename)
  6.     if ext not in ('.fastq'):
  7.         return 'File extension not allowed.'
  8.  
  9.     save_path = '/tmp/fastq_prog'
  10.     if not os.path.exists(save_path):
  11.         os.makedirs(save_path)
  12.  
  13.     file_path = "{path}/{file}".format(path=save_path, file=upload.filename)
  14.     upload.save(file_path, overwrite=True)
  15.  
  16.     new_name = '/tmp/fastq_prog/'+new_key
  17.     old_name = '/tmp/fastq_prog/'+upload.filename
  18.     os.rename(old_name, new_name)
  19.  
  20.     return template('upload', new_key='new_key')
  21.  
  22. @route('/analyse', method='POST')
  23. def analyse():
  24.     new_key = request.forms.get('new_key')
  25.  
  26.     filename = open(os.path.join('/tmp/fastq_prog/',new_key), 'r')
  27.     for line in filename:
  28.         print(line)


  1. <p>File successful uploaded</p>
  2.             <p>want to see your file with the coding was used?</p>
  3.             <p>{{new_key}}</p> <--nur zum Überprüfen, ob die Variable ans Template übergeben wurde.
  4.             <p><form action="/analyse" method="post" enctype="multipart/form-data">
  5.             <input type='hidden' name='Key' value='{{new_key}}'/>
  6.       <p><input value='Analyse' type="submit" name='analyse' /></p>
  7.             </form>


Code: Alles auswählen

Traceback (most recent call last):
  File "/home/arsenij/Dokumente/Final_project/test/bottle.py", line 862, in _handle
    return route.call(**args)
  File "/home/arsenij/Dokumente/Final_project/test/bottle.py", line 1740, in wrapper
    rv = callback(*a, **ka)
  File "bottle_test.py", line 57, in analyse
    filename = open(os.path.join('/tmp/fastq_prog/',new_key), 'r')
  File "/usr/lib/python3.5/posixpath.py", line 89, in join
    genericpath._check_arg_types('join', a, *p)
  File "/usr/lib/python3.5/genericpath.py", line 143, in _check_arg_types
    (funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'NoneType'


Noch eine Frage zu bottle. In einigen Foreneinträgen habe ich etwas vom rendern der Templates gelesen. WIe ist das zu verstehen. Wird es mich auch betreffen?
Benutzeravatar
BlackJack
Moderator
Beiträge: 31927
Registriert: Dienstag 25. Januar 2005, 23:29
Wohnort: Berlin
Kontaktdaten:

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon BlackJack » Samstag 4. Februar 2017, 22:59

@Mars_ars: ``if ext not in ('.fastq'):`` macht nicht was Du denkst was es macht:
  1. In [4]: ext = '.'
  2.  
  3. In [5]: ext not in ('.fastq')
  4. Out[5]: False


Ich verstehe auch dieses Umbenennen nicht wirklich. Statt unter einem Namen a zu speichern und dann von a nach b umzubenennen, wäre es weniger umständlich einfach gleich unter dem Namen b zu speichern.

Und warum gibtst Du den statischen Wert 'new_key' im Template zurück? Das macht keinen Sinn. Möchtest Du an der Stelle nicht vielleicht doch lieber den Wert der an den Namen `new_key` gebunden ist an das Template übergeben?

Anstelle des selbstgebastelten `new_key` würde ich mir eine passende UUID erstellen lassen.

In `analyse()` versuchst Du auf ein Formularfeld 'new_key' zuzugreifen, was es sehr offensichtlich nicht gibt. Also entweder nimmst Du denn tatsächlichen Namen des Feldes aus dem Formular oder passt den Namen dort an.
„All religions are the same:
religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 5741
Registriert: Sonntag 21. Oktober 2012, 17:20

Re: Verarbeiten einer Datei mit Python3 und Bottle

Beitragvon Sirius3 » Samstag 4. Februar 2017, 23:03

@Mars_ars: Dein Problem ist, dass Du inzwischen zwar vieles richtig machst, aber nicht konzentriert genug bist. Wie sieht die analyse-Form genau aus? Dein „Überprüfen“ scheint auch nicht geholfen zu haben.

Zurück zu „Webframeworks“

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder