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

@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

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

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:

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

@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

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.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

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 (former) Modvoice
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

@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

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

@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
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

@BJ

hab das Script mal ausprobiert => ich glaube ich verstehe es langsam

kleine Änderung:

Code: Alles auswählen

def main():
    reader = None
    try:
        target = open(os.devnull, 'wb')
        filename = 'P:\\record\\Ocean_11.mpg'
        reader = MeasuringReader(open(filename, 'rb'),
                                 os.path.getsize(filename))
        monitor = TransferMonitor(reader)
        monitor.start()
        copyfileobj(reader, target)
        target.close()
    finally:
        if reader:
            reader.close()
gruss x-herbert
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

x-herbert hat geschrieben: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??
blackbird hat seine Erfahrungen mit jQuery zusammengefasst und ich kann dem absolut zustimmen. Mit jQuery zu Arbeiten ist einfach grandios, damit bekommt man innerhalb kürzester Zeit tolle Dinge zusammen und das Chaining ist sehr nützlich.
Wenn ich noch eine Library für GUI bräuchte, würde ich Ext mit jQuery kombinieren (da gibt es extra Ext-Integration für).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

... die Sachen sehen schon recht schick aus!

zu meinem Thema:
auf dem Server läuft leider nur eine 2.2.x Version, so dass ich bei Tempfiles das NamedTemporaryFile nicht verwenden kann - meine Idee war einen "Zeiger" auf die temporäre Datei zu setzen um zu sehen wie diese Größer wird.

Als Fazit aus dem Ganzen sehe ich eigentlich nur noch, die Funktion make_file() aus FieldStorage umzubasteln und die durchgeschleusten Byte zu zählen... siehe http://osdir.com/ml/python.cherrypy/200 ... 00107.html

Ansonnsten funktioniert der dirty-hacking-code erstmal und ich muss den nochmal "nachputzen"[/code]

Änderungzu make_file: muss sicher in Funktion read_binary(self) eingebastelt werden
gruss x-herbert
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Öhm du kannst ja einfach das File was du von make_file zurück gibst mit blackjacks code wrappen O_o
BlackJack

So einfach geht's nicht, weil `make_file()` für die einzelnen übertragenen Dateien aufgerufen wird und nicht für den gesamten Datenstrom. Die Grösse so einer Datei ist also nicht gleich `CONTENT_LENGTH`.
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Die run Methode muss er sowieso anpassen. Auch wenn ich Persönlich ein Callback für hübscher halten würde als so ein gepolle :)
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

Ich denke, da gibt es einen "logischen Kurzschluss"

Nach bisherigen Kentnisstand kommen doch wohl nur zwei Varianten über die Arbeitsweise von FieldStorage:

1.) FieldStorage speichert erst den StdIn komplett in einer Temp und macht anschließend für die einzelnen Files Temps

2.) FieldStorage fischt sich anhand der Boundaries gleich aus dem StdIn einzelne Temps anhand Files-Boundaries

wenn 1.) dann sollte an der Stelle wo das erste "große" Temp erzeugt und geschrieben wird, die Bytes gezählt werden

wenn 2.) dann wäre es richtig, vorher den Datenstrom abzufangen und zu zählen

[da make_file() also tempfile bei 2.2.x keine Dateinamen zurück gibt, muss an anderer Stelle zum "Einklinken" gesucht werden]

Gruss x-herbert
gruss x-herbert
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

@veers

run-Methode? wie ist das gemeint...
gruss x-herbert
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

http://paste.pocoo.org/show/2404/ da hat die Klasse TransferMonitor eine Run Methode. Und diese müsstest du anpassen oder mit Callbacks arbeiten.
x-herbert
User
Beiträge: 59
Registriert: Mittwoch 27. November 2002, 20:52

@veers

.... o.k.!

Thread => run
gruss x-herbert
Antworten