2009-03-08 20 views
14

Wie kann ich ein Bild einfach nach dem Hochladen in Django skalieren? Ich benutze Django 1.0.2 und ich habe PIL installiert.Größe ändern bei speichern

Ich überlegte, die save() -Methode des Modells zu überschreiben, um die Größe zu ändern, aber ich weiß nicht wirklich, wie ich anfangen und es überschreiben soll.

Kann mir jemand in die richtige Richtung zeigen? Dank :-)

@ Guðmundur H: Das wird nicht funktionieren, weil das django-stdimage Paket unter Windows funktioniert nicht :-(

Antwort

12

sollten Sie eine Methode verwenden, die hochgeladene Datei zu handhaben, wie gezeigt in die Django documentation.

bei dieser Methode können Sie die Stücke in einem variablen verketten (anstatt sie auf der Festplatte direkt zu schreiben), ein PIL Bild von diesen variablen erstellen, die Größe des Bildes und auf der Festplatte speichern.

In PIL sollten Sie sich Image.fromstring und Image.resize ansehen .

22

Ich empfehle die Verwendung von StdImageField von django-stdimage, sollte es alle schmutzigen Arbeit für Sie behandeln. Es ist einfach zu bedienen, Sie geben nur die Dimensionen des skalierte Bild in der Felddefinition:

class MyModel(models.Model): 
    image = StdImageField(upload_to='path/to/img', size=(640, 480)) 

die Dokumentation Check-out - es kann auch Thumbnails tun.

+0

Dies scheint nicht auf einem Windows-Rechner zu funktionieren :-(Es ist ein bekannter Fehler im django-stdimage-Paket. Es gibt eine Art von Rekursion Fehler –

+11

Windows kann nicht recurse. Es ist ein Linux-Feature :) – Codygman

+0

funktioniert auf win32 für mich. –

6

Ich empfehle die sorl-thumbnail App für die Handhabung Bildgrößenanpassung einfach und transparent. Es geht in jedem einzelnen Django-Projekt, das ich beginne.

+0

Das Projekt [sorl-thumbnail] (https://github.com/sorl/sorl-thumbnail) wurde in github btw verschoben. – Raj

+0

Sorl hat einen neuen Mantainer und bereitet sich mit einer neuen Veröffentlichung bald darauf vor, gib ihm einen Blick: http://github.com/mariocesar/sorl-thumbnail –

11

Ich verwende diesen Code, um hochgeladene Bilder zu verarbeiten, ihre Größe im Speicher zu ändern (ohne sie dauerhaft auf der Festplatte zu speichern) und dann den Daumen auf einem Django ImageField zu speichern. Hoffnung kann helfen.

def handle_uploaded_image(i): 
     import StringIO 
     from PIL import Image, ImageOps 
     import os 
     from django.core.files import File 
     # read image from InMemoryUploadedFile 
     image_str = “” 
     for c in i.chunks(): 
      image_str += c 

     # create PIL Image instance 
     imagefile = StringIO.StringIO(image_str) 
     image = Image.open(imagefile) 

     # if not RGB, convert 
     if image.mode not in (“L”, “RGB”): 
      image = image.convert(“RGB”) 

     #define file output dimensions (ex 60x60) 
     x = 130 
     y = 130 

     #get orginal image ratio 
     img_ratio = float(image.size[0])/image.size[1] 

     # resize but constrain proportions? 
     if x==0.0: 
      x = y * img_ratio 
     elif y==0.0: 
      y = x/img_ratio 

     # output file ratio 
     resize_ratio = float(x)/y 
     x = int(x); y = int(y) 

     # get output with and height to do the first crop 
     if(img_ratio > resize_ratio): 
      output_width = x * image.size[1]/y 
      output_height = image.size[1] 
      originX = image.size[0]/2 - output_width/2 
      originY = 0 
     else: 
      output_width = image.size[0] 
      output_height = y * image.size[0]/x 
      originX = 0 
      originY = image.size[1]/2 - output_height/2 

     #crop 
     cropBox = (originX, originY, originX + output_width, originY + output_height) 
     image = image.crop(cropBox) 

     # resize (doing a thumb) 
     image.thumbnail([x, y], Image.ANTIALIAS) 

     # re-initialize imageFile and set a hash (unique filename) 
     imagefile = StringIO.StringIO() 
     filename = hashlib.md5(imagefile.getvalue()).hexdigest()+’.jpg’ 

     #save to disk 
     imagefile = open(os.path.join(‘/tmp’,filename), ‘w’) 
     image.save(imagefile,’JPEG’, quality=90) 
     imagefile = open(os.path.join(‘/tmp’,filename), ‘r’) 
     content = File(imagefile) 

     return (filename, content) 

#views.py 

    form = YourModelForm(request.POST, request.FILES, instance=profile) 
     if form.is_valid(): 
      ob = form.save(commit=False) 
      try: 
       t = handle_uploaded_image(request.FILES[‘icon’]) 
       ob.image.save(t[0],t[1]) 
      except KeyError: 
       ob.save() 
+0

UPDATE: Dies akzeptiert utf8 images filename –

+2

Ihre Methode funktioniert gut. Danke für den Code! Ich würde jedoch vorschlagen, dass Sie den obigen Variablennamen von str in etwas anderes ändern, weil er die Python-BIF-Funktion str() überschattet. Wenn jemand den Code, den Sie geschrieben haben, ein wenig ändert und das BIF unter der Variablendeklaration verwendet, würde dies einen Fehler verursachen (Python würde sagen, dass str nicht aufrufbar ist) – rvnovaes

+0

Aktualisiert, danke! –

2

Ich weiß, das ist alt, aber für jemand auf sie stolpern, es ist ein Paket, django-thumbs bei Django-thumbs - Easy powerful thumbnails for Django integrated with StorageBackend, die Sie Thumbnails in den Größen angeben automatisch erzeugt oder keine, wenn Sie dies nicht tun. Sie rufen dann das gewünschte Miniaturbild mit den gewünschten Dimensionen auf.

Wenn Sie zum Beispiel möchten, dass ein Bild Thumbnails von 64x64 und 128x128 hat, importieren Sie einfach und verwenden es anstelle von ImageField. Fügen Sie einen Parameter sizes=((64,64),(128,128)) auf die Felddefinition, dann aus Ihrer Vorlage können Sie anrufen:

{{ ClassName.field_name.url_64x64 }} 

und

{{ ClassName.field_name.url_128x128 }} 

die Thumbnails angezeigt werden soll. Voila! Die ganze Arbeit ist in diesem Paket für Sie erledigt.

+0

Wenn Sie Süd für die Pflege der Datenbank verwenden, werden Sie auch eine Innerlichkeit, als solche hinzufügen müssen [], # Positional Argumente (nicht benutzt) {# Keyword Argument "Größen": [ "Größen", { "default": keine}], }, ), ], [ "^ django_thumbs \. db \ .models \ .ImageWithThumbsField "])' – Furbeenator

+0

'von south.modelsinspector Import add_introspection_rules add_introspection_rules ([ ( [models.ImageField], # Klasse (n) diese gelten für: – Furbeenator

2

Hier ist eine vollständige Lösung für die Verwendung eines Formulars. Ich benutzte Admin-Ansichten für diese:

class MyInventoryItemForm(forms.ModelForm): 

    class Meta: 
     model = InventoryItem 
     exclude = ['thumbnail', 'price', 'active'] 

    def clean_photo(self): 
     import StringIO 
     image_field = self.cleaned_data['photo'] 
     photo_new = StringIO.StringIO(image_field.read()) 

     try: 
      from PIL import Image, ImageOps 

     except ImportError: 
      import Image 
      import ImageOps 

     image = Image.open(photo_new) 

     # ImageOps compatible mode 
     if image.mode not in ("L", "RGB"): 
      image = image.convert("RGB") 

     image.thumbnail((200, 200), Image.ANTIALIAS) 

     image_file = StringIO.StringIO() 
     image.save(image_file, 'png') 

     image_field.file = image_file 

     return image_field 

Mein Inventar Modell sieht wie folgt aus:

class InventoryItem(models.Model): 

    class Meta: 
     ordering = ['name'] 
     verbose_name_plural = "Items" 

    def get_absolute_url(self): 
     return "/products/{0}/".format(self.slug) 

    def get_file_path(instance, filename): 

     if InventoryItem.objects.filter(pk=instance.pk): 
      cur_inventory = InventoryItem.objects.get(pk=instance.pk) 
      if cur_inventory.photo: 
       old_filename = str(cur_inventory.photo) 
       os.remove(os.path.join(MEDIA_ROOT, old_filename)) 

     ext = filename.split('.')[-1] 
     filename = "{0}.{1}".format(uuid.uuid4(), ext) 
     return os.path.join('inventory', filename) 
     #return os.path.join(filename) 

    def admin_image(self): 
     return '<img height="50px" src="{0}/{1}"/>'.format(MEDIA_URL, self.photo) 
    admin_image.allow_tags = True 

    photo = models.ImageField(_('Image'), upload_to=get_file_path, storage=fs, blank=False, null=False) 
    thumbnail = models.ImageField(_('Thumbnail'), upload_to="thumbnails/", storage=fs,  blank=True, null=True) 

....

I ended Überschreiben die Funktion des Modells speichern, anstatt das Foto und einen Daumen zu speichern, anstatt Ändern der Größe von gerade das Foto:

def save(self): 

    # Save this photo instance first 
    super(InventoryItem, self).save() 

    from PIL import Image 
    from cStringIO import StringIO 
    from django.core.files.uploadedfile import SimpleUploadedFile 

    # Set our max thumbnail size in a tuple (max width, max height) 
    THUMBNAIL_SIZE = (200, 200) 

    # Open original photo which we want to thumbnail using PIL's Image object 
    image = Image.open(os.path.join(MEDIA_ROOT, self.photo.name)) 

    if image.mode not in ('L', 'RGB'): 
     image = image.convert('RGB') 

    image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS) 

    # Save the thumbnail 
    temp_handle = StringIO() 
    image.save(temp_handle, 'png') # image stored to stringIO 

    temp_handle.seek(0) # sets position of file to 0 

    # Save to the thumbnail field 
    suf = SimpleUploadedFile(os.path.split(self.photo.name)[-1], 
     temp_handle.read(), content_type='image/png') # reads in the file to save it 

    self.thumbnail.save(suf.name+'.png', suf, save=False) 

    #Save this photo instance again to save the thumbnail 
    super(InventoryItem, self).save() 

Beide arbeiten toll, aber je nachdem, was Sie tun wollen :)

Verwandte Themen