Array aus Form

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Freitag 10. August 2007, 08:15

@VEERS => Nachtrag

was bezweckst Du mit

Code: Alles auswählen

... os.sep in fileitem.filename
??
gruss x-herbert
BlackJack

Beitragvon BlackJack » Freitag 10. August 2007, 08:33

Vielleicht solltest Du mal einen Blick in das `cgi`-Modul werfen und dort insbesondere in die `FieldStorage`-Klasse. Die `make_file()`-Methode könnte interessant sein. Die ist im Docstring als "überschreibbar" beschrieben, wenn man selber festlegen möchte wo die temporären Dateien landen.

Auf jeden Fall kannst Du in dem Modul genau sehen was Python bei CGI macht und wie es gemacht wird.

Zum `os.sep`: Ich denke veers möchte verhindern, dass Dateinamen mit Pfadtrennern gespeichert werden, weil die entweder in einem Unterverzeichnis landen würden, was es nicht gibt, oder aber ein böser Mensch mit so etwas wie '../../index.html' versucht irgendwelche Dateien der Website zu überschreiben.
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Freitag 10. August 2007, 09:04

@BlackJack

ich würde ja mit dem os.sep nicht so rigoros umgehen und gleich ein continue einbauen - kann ja auch mal am Browser liegen... vielleicht ist das rausfischen des eigentlichen Dateinames besser (os.path.basename)...

das cgi-modul werde ich mir mal ansehen - ich hatte bisher ein perl-Script in Python nachgebastelt da Perl (warum auch immer) nach ca 16MB Upload ausstieg - Python schaffte über 65MB...

Für das Polling gibt es natürlich verschiedene Möglichkeiten wie z.B. während des Einlesens des StdIn eine Statusdatei schreiben mit kB, Zeit, % usw. oder die Sachen "von außen" über ein anderes Script anhand der Temp-Datei "beobachten" - hat beides sein "für-und-wider".
gruss x-herbert
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Beitragvon veers » Freitag 10. August 2007, 17:09

BlackJack hat geschrieben:Zum `os.sep`: Ich denke veers möchte verhindern, dass Dateinamen mit Pfadtrennern gespeichert werden, weil die entweder in einem Unterverzeichnis landen würden, was es nicht gibt, oder aber ein böser Mensch mit so etwas wie '../../index.html' versucht irgendwelche Dateien der Website zu überschreiben.
Ja, hatte ich damit vor. Ist aber anscheinend nicht nötig. Ich versuche noch herauszufinden wo das gemacht wird.

@x-herbert,
Ich verwende nun basename das ist wohl schöner. Sowie eine Ausnahme für "bestimmte" Browser welche meinen in filename den ganzen Pfad stecken zu müssen.

Zu deinem 65 mb, ich konnte Files bis zu 2gb Problemlos hochladen ;)
BlackJack

Beitragvon BlackJack » Freitag 10. August 2007, 17:35

Ich weiss es nicht genau, aber ich vermute mal das ist auch keine Beschränkung von Perl sondern irgendwo eine Einstellung, vielleicht sogar beim Webserver, um zu verhindern das jemand von aussen mit überdimensionalen Uploads die Platte zumüllen kann.
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Samstag 11. August 2007, 11:38

@BlackJak

... Beschränkung des Servers hatte ich auch angenommen - konnte aber beim Googlen nix explizites für Perl finden - Python und Perl arbeiten ja auf der selben Schnittstelle (CGI) und da sollte dies i.e. gleich sein oder??
gruss x-herbert
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beta Dirty Hacking!!!

Beitragvon x-herbert » Samstag 11. August 2007, 12:17

also hier der Beta Dirty Hacking Code

Code: Alles auswählen

#!/usr/bin/python

import os, sys, time, glob, string
import md5
import cgi
import cgitb

cgitb.enable()

upload_path = os.environ['DOCUMENT_ROOT'] + 'uploads/'
cgi_tmp_file_name = 'cgi_data_tmp_'
message = ''

def readStdin(fnpath, fn, sid):
      content_len = int(os.environ['CONTENT_LENGTH']) * 1.
      t_start = time.time()
      
      fstat = open(fnpath + "stats_" + sid + '.txt', 'w')
      f = open(fnpath + fn + sid, 'w+')
      b_read = 0
      while True:
            data = sys.stdin.read(1024*64) # read 64k at a time
            if not data: break
            f.write(data)
            f.flush() # so the ajax poller gets an accurate reading
            
            b_read += 1024*64
            t_elapsed = time.time() - t_start
            
            if t_elapsed >= 1:
                  b_estimate = b_read / t_elapsed; # in kb/sec
            else:
                  b_estimate = b_read / .5;

            b_percent = b_read / content_len * 100. # content_len in kb
            if b_percent >= 99:
                  b_percent = 99            
            t_est = (content_len - b_read) / b_estimate # in sec
            if t_est <= 0.:
                  t_est = 0.
            b_est_percent = b_estimate / content_len * 100. # in percent kb

            # % , % per second, kb estimate, est time remaining
            fstat.seek(0,0)
            fstat.write("%f\n%f\n%f\n%f\n%d\n\n" % (b_percent,b_est_percent,b_estimate,t_est,content_len))
            fstat.flush()               
            time.sleep(0.35)
      fstat.seek(0,0)
      fstat.write("100.\n%f\n%f\n0.\n%d\n\n" % (b_est_percent,b_estimate,content_len))
      fstat.flush()
      fstat.close()
      
      f.seek(0)
      return f
      
def writeFileData(fnpath, fi):

      # strip leading path from file name to avoid directory traversal attacks
      fn = os.path.basename(fi.filename)
      path_fn = fnpath + fn

      fout_fn = open(path_fn, 'w')
      while 1:
            chunk = fi.file.read(4096)
            if not chunk:   break
            fout_fn.write(chunk)
      fout_fn.close()
      
      if os.path.exists ( path_fn ):
            oldumask = os.umask(0)
            os.chmod( path_fn, 0755 )
            os.umask( oldumask )
               
      return

def killFiles(fnpath, fn, sid):
      try:
            os.remove(fnpath + fn + sid)
      except:
         pass
      return
         

# #MAIN# #

# HTMLForm_fileupload.cgi?sid=val
try:
   session_id = cgi.parse_qs(os.environ['QUERY_STRING'])['sid'][0]
except:
   session_id = "nosid_nosid"

# read in data over stdin and write to a file as the file uploads
cgi_in = readStdin(upload_path, cgi_tmp_file_name, session_id)

form = cgi.FieldStorage(cgi_in)
cgi_in.close()


if form:
      f = 0
      if form.has_key('file[]'):  # we have uploads.
            for fileitem in form['file[]']:
                  if fileitem.file and fileitem.filename:
                        writeFileData(upload_path, fileitem)
                        f +=1
                        message += 'Datei %d uploaded %s<br>' % (f, str(fileitem.filename))
            if f:
                  killFiles(upload_path, cgi_tmp_file_name, session_id)
            else:
                  message = 'keine Datei angegeben'
      else:
            message = 'keine Dateifeld'
else:
      message = "keine Formulardaten eingegangen"

print """\
Content-Type: text/html\n
<html><body>"""

print "<p>%s</p></body></html>" % (message,)


Die stats_sid.txt wird per AJAX als Progressbar angezeigt...

Weitere Dateibehandlung usw über andere Scripte....
gruss x-herbert
BlackJack

Beitragvon BlackJack » Samstag 11. August 2007, 13:51

Hier wird für meinen Geschmack einmal zuviel kopiert. Das `readStdin()` speichert die Daten auf Platte, `cgi.FieldStorage()` kopiert diese Datei nochmal in temporäre Dateien und dann werden die mit `writeFileData()` *nochmal* kopiert.

Es wäre vielleicht effizienter wenn man `sys.stdin` in einem Datei-ähnlichem Objekt kapselt, das einfach die gelesenen Bytes mitzählt und Methoden zur Abfrage der "Statistik" zur Verfügung stellt.

Das AJAX-Pollen von der Datei ist ein bisschen fragil. In den allermeisten Fällen werden die paar Daten wahrscheinlich atomar geschrieben, aber sauber ist das nicht. Und ich bin mir nicht so sicher ob man die Dateien für die Daten nicht besser als Binärdateien öffnet!?

Ansonsten: ``float(string)`` ist etwas einfacher und direkter als ``int(string) * 1.``

Zu `b_read` würde ich die tatsächlich gelesene Anzahl von Bytes addieren und keine Konstante.

Pfadnamen mit `os.path.join()` zusammensetzen.

Für's kopieren von Dateien gibt's im Modul `shutil` fertige Funktionen.

``return`` ohne alles ist am Ende einer Funktion nicht nötig.

Bei ``'%s' % str(obj)`` ist das `str()` überflüssig. Diese Umwandlung macht das '%s' schon.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Beitragvon veers » Samstag 11. August 2007, 16:13

Ein weg wäre ja make_file im cgi Modul zu überladen. In dem File was du da zurück gibst kannst du dich dann in die write Methode hängen.

Die alternative wäre natürlich einfach per Ajax die Grösse der hochgeladenen Datei zu messen. Vermutlich das einfachste ;)
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Sonntag 12. August 2007, 13:01

@BlackJack
mit float, b_read, return und %s hast Du natürlich recht!!

Zum Thema Ajax-Polling:
es gibt (wie schon erleutert) zwei generelle Wege:
1.) ich schreibe eine Statistikdatei (wie hier)
2.) ich "beobachte" die Temp-Datei von außen

Generell würde ich auch Punkt 2 vorziehen, aber ich muss die Sachen in ein bestehendes Projekt einfügen und muss sukzessive die Dinge ändern - daher erstmal stats_sid.txt.

Beim Beobachten von Außen muss ich (warscheinlich ??) irgendwo die CONTENT_LENGHT ablegen, um die 100%-Bezugsgröße zu haben.

Frage: Könnte man nicht in dem vorliegenden Fall das StdIn durchschleusen, zählen und direkt an cgi.FieldStorage weitergeben
geht sowas wie

Code: Alles auswählen

def umleitung(dateistromstückchen):
  zähle bits
  return dateistromstückchen

form = cgi.FieldStorage(umleitungsys.stdin.read(1024))


Übrigens, ich konnte nicht erkennen, dass cgi.FieldStorage nochmal eine temporäre Datei anlegt. Die Reihenfolge dürfte genau umgekehrt sein: writeFileData() legt eine temp. Datei an, die dann den Defaultparameter fp (stdin) von FieldStorage überschreibt.

Thema: Öffnen als Binärdateien - ich habe beides gefunden als w bzw. wb und keine eindeutige Aussage, dass man unbedingt wb nehmen muss...

Ich bin keinerlei "Beratungsresitent" und kein großer "Hirsch" in Python dass ich nicht noch ´ne Menge lernen könnte!! Da Python (meiner Meinung nach) gegenüber Ruby den Zug "AJAX" etwas verpasst hat, wäre hier die Gelegenheit ein Stück aufzuholen....

["Überholen ohne Aufzuholen" {Walter Ulbricht 1970} hate zwar schon damals nicht funktioniert, aber man sollte es probieren...]
gruss x-herbert
BlackJack

Beitragvon BlackJack » Sonntag 12. August 2007, 13:16

Genau das mit der Umleitung hatte ich doch schon vorgeschlagen: Ein Datei-ähnliches Objekt, das mitzählt wieviel gelesen wurde.

Zu den Kopien: Du liesst den Datenstrom von `sys.stdin` ein und schreibst ihn in eine Datei. Diese Datei wird an `FieldStorage` übergeben und geparst, dabei werden die enthaltenen Dateien in temporären Dateien abgelegt. Und diese temporären Dateien werden dann noch einmal von Deinem Code an die endgültige Position kopiert. Und das erste zwischenspeichern müsste man sich sparen können.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Sonntag 12. August 2007, 14:00

x-herbert hat geschrieben:Thema: Öffnen als Binärdateien - ich habe beides gefunden als w bzw. wb und keine eindeutige Aussage, dass man unbedingt wb nehmen muss...

Muss man aber, zumindest unter Windows, wo es einen Unterschied zwischen Text und Binärdateien gibt. Wenn du dort Binärdateien ohne das ``b`` Flag schreibst sind sie Schrott, weil die EOL-Zeichen platformspezifisch konvertiert werden.

x-herbert hat geschrieben:Da Python (meiner Meinung nach) gegenüber Ruby den Zug "AJAX" etwas verpasst hat, wäre hier die Gelegenheit ein Stück aufzuholen....

Ich finde AJAX in sagen wir mal, Django besser als in Rails. Warum? Weil es nicht mitgeliefert ist und ich somit entscheiden kann, ob ich Prototype einsetzen will - will ich meist nicht, bevorzuge jQuery.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Montag 13. August 2007, 08:38

@BlacJack
Genau das mit der Umleitung hatte ich doch schon vorgeschlagen: Ein Datei-ähnliches Objekt, das mitzählt wieviel gelesen wurde.


upps.... da reißt der Windschatten langsam ab - da habe ich keine Idee wie man dies realisiert :-(

@Leonidas:
danke für den Tipp Binary vs. Windows...

Bisher bin ich mit Prototype gut über die Runden gekommen - bei Fehlern lag das an mir und nicht an Prototype.. ;-)

Ich habe mir jQuery nochmal genauer angesehen - macht auch einen guten Eindruck... besser/schlechter ist hier wohl kaum die Frage, sondern auf welches "Pferd" man setzt und Erfahrungen sammelt - ODER gibt es von Dir noch USPs zu jQuery??
gruss x-herbert
BlackJack

Beitragvon BlackJack » Montag 13. August 2007, 10:13

Ich habe mal so ein Dateiobjekt zusammengebastelt. "Überwachung" und Ausgabe ist mit einem Thread realisiert.

http://paste.pocoo.org/show/2404/
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Beitragvon x-herbert » Montag 13. August 2007, 10:16

@BJ

habe das ober Posting jetzt erst gesehen...

Oh!
--- ich blick nicht durch ---

~~~~~~~~~~~~~~~~~~~~~~~~

Thema temporäre Dateispeicherung:

ich habe nochmal in das cgi.py gesehen - zwar nicht alles verstanden, aber mir scheint, dass hier aus dem Datenstrom StdIn sofort einzelne Temp-Dateien für die einzenlnen Multipartteile erstellt werden?

Code: Alles auswählen

    def read_multi(self, environ, keep_blank_values, strict_parsing):
        """Internal: read a part that is itself multipart."""
        ib = self.innerboundary
        if not valid_boundary(ib):
            raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,)
        self.list = []
        klass = self.FieldStorageClass or self.__class__
        part = klass(self.fp, {}, ib,
                     environ, keep_blank_values, strict_parsing)
        # Throw first part away
        while not part.done:
            headers = rfc822.Message(self.fp)
            part = klass(self.fp, headers, ib,
                         environ, keep_blank_values, strict_parsing)
            self.list.append(part)
        self.skip_lines()

    def read_single(self):
        """Internal: read an atomic part."""
        if self.length >= 0:
            self.read_binary()
            self.skip_lines()
        else:
            self.read_lines()
        self.file.seek(0)

    bufsize = 8*1024            # I/O buffering size for copy to file

    def read_binary(self):
        """Internal: read binary data."""
        self.file = self.make_file('b')
        todo = self.length
        if todo >= 0:
            while todo > 0:
                data = self.fp.read(min(todo, self.bufsize))
                if not data:
                    self.done = -1
                    break
                self.file.write(data)
                todo = todo - len(data)
...
   def make_file(self, binary=None):
        """Overridable: return a readable & writable file.
            ...
        """
        import tempfile
        return tempfile.TemporaryFile("w+b")
gruss x-herbert

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder