2009-03-08 21 views
14

Jak mogę łatwo zmienić rozmiar obrazu po jego przesłaniu w Django? Używam Django 1.0.2 i zainstalowałem PIL.zmiana rozmiaru obrazu przy zapisie

Zastanawiam się nad przesłonięciem metody save() Modelu, aby zmienić jej rozmiar, ale tak naprawdę nie wiem jak zacząć i nadpisać.

Czy ktoś może wskazać mi właściwy kierunek? Dzięki :-)

@ Guðmundur H: To nie zadziała, ponieważ pakiet django-stdimage nie działa na Windows :-(

Odpowiedz

12

Należy stosować metodę obsłużyć przesłanego pliku, jak pokazano w Django documentation.

w tej metodzie, można złączyć kawałki w zmiennej (zamiast pisać je na dysku bezpośrednio), utwórz PIL obrazu z tej zmiennej, zmienić rozmiar obrazu i zapisać go na dysku.

W PIL powinieneś spojrzeć na Image.fromstring i Image.resize .

22

Polecam używanie StdImageField z django-stdimage, powinien obsłużyć całą brudną robotę dla ciebie. Jest łatwy w użyciu, wystarczy podać wymiary zmienionym rozmiarze w definicji pola:

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

Wyjazd docs - może to zrobić również miniaturki.

+0

Wygląda na to, że nie działa na komputerze z systemem Windows :-(Jest to znany błąd w pakiecie django-stdimage Istnieje pewien rodzaj błędu rekursji –

+11

System Windows nie może być rekurencyjny - jest to funkcja dostępna tylko w systemie Linux :) – Codygman

+0

działa na win32 dla mnie. –

6

Bardzo polecam aplikację sorl-thumbnail do łatwej i przejrzystej zmiany rozmiaru obrazu. Odbywa się w każdym projekcie Django, który rozpoczynam.

+0

Projekt [sorl-thumbnail] (https://github.com/sorl/sorl-thumbnail) został przeniesiony do github btw. – Raj

+0

Sorl ma nowy mantainer i przygotowuje się wkrótce do wydania, zobacz go http://github.com/mariocesar/sorl-thumbnail –

11

Używam tego kodu do obsługi przesyłanych obrazów, zmiany rozmiaru ich w pamięci (bez zapisywania ich na stałe na dysku), a następnie zapisywania kciuka w Django ImageField. Nadzieja może pomóc.

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: To zaakceptuje utf8 images nazwa pliku –

+2

Twoja metoda działa dobrze. Dzięki za kod! Sugerowałbym jednak, żebyś zmienił nazwę zmiennej powyżej ze str na coś innego, ponieważ cienie funkcji Pythona BIF str(). Jeśli ktoś zmieni nieco kod, który napisałeś i użyje BIF poniżej deklaracji zmiennej, spowodowałby błąd (python powiedziałby, że str nie jest dostępny) – rvnovaes

+0

Zaktualizowany, dziękuję! –

2

wiem, że to jest stary, ale dla każdego, natrafiają na nią, nie jest to pakiet, django-thumbs na Django-thumbs - Easy powerful thumbnails for Django integrated with StorageBackend, który automatycznie generuje miniaturki rozmiary określone przez użytkownika, albo żadna, jeśli nie. Następnie wybierasz żądaną miniaturę z żądanymi wymiarami.

Na przykład, jeśli chcesz, aby obraz miał miniatury 64 x 64 i 128 x 128, po prostu zaimportuj thumbs.models.ImageWithThumbsField i użyj go zamiast ImageField. Dodaj parametr sizes=((64,64),(128,128)) do definicji pola, a następnie od szablonu można nazwać:

{{ ClassName.field_name.url_64x64 }} 

i

{{ ClassName.field_name.url_128x128 }} 

aby wyświetlić miniatury. Voila! Cała praca jest wykonana w tym pakiecie dla ciebie.

+0

Jeśli używasz Południowej za utrzymanie bazy danych, będzie trzeba także dodać introspekcję, jako takich: – Furbeenator

+0

'z south.modelsinspector add_introspection_rules przywozowych add_introspection_rules ([ ( [models.ImageField] # Klasa (-y) stosuje się do nich [], # pozycyjne argumenty (niewykorzystane) {# słowa argumentów "wielkości" [ "Powierzchnie" { "domyślne": Brak}] } ) ], [ "^ django_thumbs \. db \ .models \ .ImageWithThumbsField "])' – Furbeenator

2

Oto kompletne rozwiązanie dla ciebie za pomocą formularza. Kiedyś widoki administratora dla tego:

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 

Mój model inwentaryzacji wygląda następująco:

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) 

....

skończyło nadpisywania funkcji modelu zamiast oszczędzać, aby zapisać zdjęcie i kciukiem, a nie tylko zmiana rozmiaru pliku:

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() 

zarówno do pracy super, mimo że w zależności od tego, co chcesz zrobić :)