Dateupload

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
kmansour
User
Beiträge: 9
Registriert: Mittwoch 14. Februar 2007, 15:07

Freitag 7. Dezember 2007, 09:24

Ich habe leider keine verwertbare Antwort bisher hier gesehen, drum der Thread.

Folgendes eigentlich simples Problem:
Ich habe eine Webseite mit einem Formular
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Felder für Datei-Upload definieren</title>
</head>
<body>

<h1>Schicken Sie uns was Schickes!</h1>

<form action="imageresizer.py" method="post" enctype="multipart/form-data">
<p>Select a picture (max. size: 2MB):<br>
<input name="file" type="file" size="50" maxlength="2000000" accept="image/*">
</p>
<input type="submit" value=" Absenden ">
<input type="reset" value=" Abbrechen">
</form>

</body>
</html>
Ich will nun eine Datei auswählen, diese uploaden und anschließend verarbeiten.
Wie ich gesehen habe gibt es dazu mod_python, cgi, wsgi und tausend andere dinge.
Das ist etwas problematisch, denn ich möchte es gerne allgemein halten. Also habe ich erst einmal versucht die verarbeitung wo anders hin zu verlagern und will nun successive für mod_python und wsgi jeweils eine "entrymethode" definieren.

Bei mod_python fängt das Problem an:

Code: Alles auswählen

#entry
#1:gucken ob ein file da ist
#2:gucken ob es kleiner 3 mb ist
#3:einlesen der daten
#4:uebergeben an verarbeitungsmethode
def handler(req):
  from mod_python import apache, util
  form = util.FieldStorage(req)
  if form.has_key('file'):
    maxMB=3*1024*1024
    if req.headers_in.has_key('content-length'):
      if int(req.headers_in['content-length'])>maxMB:
        req.write('Error Filesize exeeded 3MB')
        req.log_error('filesize to big')
        return apache.OK
    fileitem = form['file']
    if not fileitem.file: return
    #data=fileitem.file.read()
    data=fileitem.value
    imageio = StringIO.StringIO(data)#data)
    #imageio.write(data)
    #imagestring=imageio.getvalue()
    newimage=createImageFromString(imageio,(400,1600))
    returnImage(req,newimage)
    #req.write(fout.getvalue())    
    imagestring.close()    
    return apache.OK
  return apache.HTTP_ERROR
wie man sehen kann habe ich da schon rumgespielt. Mein Problem:
Obiger Code gibt mit am Ende den DateiNAMEN zurück !
Wenn ich allerdings die main methode aufrufe, DANN bekomme ich ein konkretes Bild abgespeichert.
Ich denke es ist (wie üblich bei mir) irgendwo ein dummer Fehler drin der ganz offensichtlich ist.

Wäre dankbar für Hilfe.

Gruß


Hier der komplette Code:

Code: Alles auswählen

#!/usr/bin/python
from PIL import Image
import StringIO

def aspectResize(image,newsize,resample):
  x, y = image.size  
  #resize the first that is smaller than the target first
  #then shrink again to make the second dimension fit
  if x < newsize[0]: 
    y = max(y * newsize[0] / x, 1)
    x = newsize[0]
    if y > newsize[1]:  
      x = max(x * newsize[1] / y, 1)
      y = newsize[1]
  elif y < newsize[1]: 
    x = max(x * newsize[1] / y, 1)
    y = newsize[1]
    if x > newsize[0]: 
      y = max(y * newsize[0] / x, 1)
      x = newsize[0]
  
  size = x, y

  if size == image.size:
    return image
  
  try:
    im = image.resize(size, resample)
  except ValueError:
    if resample != ANTIALIAS:
      raise
    im = image.resize(size, NEAREST)
  return im

#used when newsize is only given for one dimension
def clearUndefinedSize(oldsize,newsize):
  if newsize[0]==0:
    newsize[0]=oldsize[0]
  if newsize[1]==0:
    newsize[1]=oldsize[1]
  return newsize

def createImageFromString(file,newsize,aspectRatio=True):
  image = Image.open(file) 
  oldsize=image.size
  if oldsize[0]>newsize[0] or oldsize[1]>newsize[1]:
    newsize=clearUndefinedSize(oldsize,newsize)
    image.thumbnail(newsize,Image.ANTIALIAS)
  else:
    newsize=clearUndefinedSize(oldsize,newsize)
    image=aspectResize(image,newsize,Image.ANTIALIAS)
 
  return image

def returnImage(req,image):
  req.content_type = 'image/jpg'
  req.send_http_header()
  req.write(image.tostring())
  return apache.OK

def handler(req):
  from mod_python import apache, util
  form = util.FieldStorage(req)
  if form.has_key('file'):
    maxMB=3*1024*1024
    if req.headers_in.has_key('content-length'):
      if int(req.headers_in['content-length'])>maxMB:
        req.write('Error Filesize exeeded 3MB')
        req.log_error('filesize to big')
        return apache.OK
    fileitem = form['file']
    if not fileitem.file: return
    #data=fileitem.file.read()
    data=fileitem.value
    imageio = StringIO.StringIO(data)#data)
    #imageio.write(data)
    #imagestring=imageio.getvalue()
    newimage=createImageFromString(imageio,(400,1600))
    returnImage(req,newimage)
    #req.write(fout.getvalue())    
    imagestring.close()    
    return apache.OK
  return apache.HTTP_ERROR


if __name__ == '__main__':
  file = open('test.jpg','rb')
  fileString = StringIO.StringIO(file.read())
  newimage=createImageFromString(fileString,(400,1600))
  newimage.save('c:/test2.jpg')
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Freitag 7. Dezember 2007, 09:40

kmansour hat geschrieben:Ich will nun eine Datei auswählen, diese uploaden und anschließend verarbeiten.
[...]
für mod_python und wsgi
Hallo kmansour!

Wenn du statt für jedes System extra etwas zu stricken, einfach mit CherryPy http://www.cherrypy.org/ arbeitest, dann brauchst du dich nicht mehr um die ganzen Einzelsysteme zu kümmern.

CherryPy läuft hinter mod_python genau so gut wie hinter mod_fastcgi. Es läuft auch als Standalone-Server und ist WSGI. Das hier http://www.cherrypy.org/browser/trunk/c ... cpmodpy.py ist z.B. für mod_python zuständig.

Und zu deinem Problem gibt es sogar ein eigenes Kapitel im Tutorial: http://www.cherrypy.org/browser/trunk/c ... 9_files.py

Und wenn ich dich damit schon überzeugen konnte, dann findest du hier eine kleine Einführung: http://www.python-forum.de/topic-10807.html ;-)

mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
BlackJack

Freitag 7. Dezember 2007, 09:53

Am Rande: Die `thumbnail()`-Methode auf PIL-Bildern verändert die Grösse unter Beibehaltung des Seitenverhältnisses. Das braucht man also nicht selbst implementieren.
kmansour
User
Beiträge: 9
Registriert: Mittwoch 14. Februar 2007, 15:07

Freitag 7. Dezember 2007, 10:57

gerold hat geschrieben: Hallo kmansour!

Wenn du statt für jedes System extra etwas zu stricken, einfach mit CherryPy http://www.cherrypy.org/ arbeitest, dann brauchst du dich nicht mehr um die ganzen Einzelsysteme zu kümmern.
Hallo gerold,
habe bisher einen Bogen um die Frameworks gemacht da es a) einige gibt und b) ich bisher kein sonderlichen Bedarf hatte.
Nun habe ich einfach mal CherryPy probiert.
Selbes Resultat !

Hier der neue Code (Rest ist gleich geblieben aus dem Tutorial):

Code: Alles auswählen

 def upload(self, myFile):
        out = """<html>
        <body>
            myFile length: %s<br />
            myFile filename: %s<br />
            myFile mime-type: %s
        </body>
        </html>"""
        
        errorout = """<html>
        <body>
             %s<br />
        </body>
        </html>"""
        
        # Although this just counts the file length, it demonstrates
        # how to read large files in chunks instead of all at once.
        # CherryPy uses Python's cgi module to read the uploaded file
        # into a temporary file; myFile.file.read reads from that.
        size = 0
        
        data = myFile.file.read()
        if not data:
          return errorout % ('No File uploaded')
        size += len(data)
        if size >3000000:
          return errorout % ('File too big (>3MB)')
        imageio = StringIO.StringIO(data)
        newimage=ir.createImageFromString(imageio,(400,1600))
        response = cherrypy.response
        response.headers['Content-Type'] = 'image/jpeg'
        #return response
        print newimage.tostring()
        return newimage.tostring()
Interessant ist nun folgendes:
1) Im browser steht nur als Ergebnis: http://localhost:8080/upload
2) Schaue ich mir aber die "Print"-Ausgabe an, dann sehe ich einen Haufen kryptischen ASCII Zeugs. Auch im Browser sehe ich das, wenn ich au Seitenquelltext gehe. Speichern und laden als Bild geht allerdings nicht

@BlackJack:
thumbnails geht nur beim verkleinern richtig dachte ich.
Dehalb implementierte ich es ja nur für die Vergrößerung.
BlackJack

Freitag 7. Dezember 2007, 11:17

Argh, ich sehe gerade das Du gar nichts sagst *wie* das Bild in Bytes umgewandelt werden soll. Wenn Du JPEG haben möchtest, musst Du das der `tostring()`-Methode auch mitteilen. Sonst bekommst Du einfach nur die "rohen" Pixeldaten.
kmansour
User
Beiträge: 9
Registriert: Mittwoch 14. Februar 2007, 15:07

Freitag 7. Dezember 2007, 11:35

BlackJack hat geschrieben:Argh

...genau das habe ich auch gerade gesagt. Laut genug wohl dass einige Kollegen reingekommen sind.

Danke :)

Jetzt geht es (auch bei der alten Version) !
Benutzeravatar
gerold
Python-Forum Veteran
Beiträge: 5555
Registriert: Samstag 28. Februar 2004, 22:04
Wohnort: Oberhofen im Inntal (Tirol)
Kontaktdaten:

Freitag 7. Dezember 2007, 11:57

Hallo!

Wie ich sehe, bin ich mit meiner Lösung des Problems ein wenig zu spät. :cry:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-
# cpimage.py
"""
Requirements
    - Python: http://www.python.org/
    - CherryPy: http://cherrypy.org/
    - PIL: http://www.pythonware.com/products/pil/
"""

import cherrypy
from PIL import Image
from StringIO import StringIO
from textwrap import dedent


class Root(object):
    
    def index(self):
        html = dedent("""
        <html>
        <body>
          <form action="upload" method="post" enctype="multipart/form-data">
            <p><input name="imagefile" type="file"></p>
            <p><input type="submit" value="Absenden"></p>
          </form>
        </body>
        </html>
        """).strip()
        return html
    index.exposed = True
    
    
    def upload(self, imagefile = None):
        cherrypy.response.headers['Content-Type'] = 'image/jpeg'
        image = Image.open(imagefile.file)
        image.thumbnail((100, 100))
        thumbnail = StringIO()
        image.save(thumbnail, "JPEG")
        return thumbnail.getvalue()
    upload.exposed = True


def main():
    app = cherrypy.tree.mount(Root())
    cherrypy.quickstart(app)
    

if __name__ == "__main__":
    main()
mfg
Gerold
:-)
http://halvar.at | Kleiner Bascom AVR Kurs
Wissen hat eine wunderbare Eigenschaft: Es verdoppelt sich, wenn man es teilt.
kmansour
User
Beiträge: 9
Registriert: Mittwoch 14. Februar 2007, 15:07

Freitag 7. Dezember 2007, 12:31

Nur damit ich das noch richtig verstehe.
Es stimmt doch, dass thumbnails nur zum Verkleinern taugt ja ?
**Edit** Ja ist so:)
Antworten