7

ja odziedziczyliśmy Django Projekt i przeszliśmy obrazów S3Django: zmiana rozmiaru zdjęcia i przesłać do S3

Jednym z modeli jest typowy profil użytkownika

class Profile(UUIDBase): 

    first_name = models.CharField(_("First Name"), max_length=20) 
    last_name = models.CharField(_("Last Name"), max_length=20, null=True) 
    profile_image = models.ImageField(
     _("Profile Image"), 
     upload_to=profile_image_name, 
     max_length=254, 
     blank=True, 
     null=True 
    ) 
    profile_image_thumb = models.ImageField(
     _("Profile Image Thumbnail"), 
     upload_to=profile_image_name, 
     max_length=254, 
     blank=True, 
     null=True 
    ) 
    ... other fields 

Gdzie profile_image_name jest funkcją :

def profile_image_name(instance, filename): 
    if filename: 
     target_dir = 'uploads/profile_img/' 
     _, ext = filename.rsplit('.', 1) 
     filename = str(instance.uid) + '.' + ext 
     return '/'.join([target_dir, filename]) 

mam kawałek kodu, który pracował:

@shared_task 
def resize_image(image_path, dim_x, append_str='_resized', **kwargs): 
    ''' 
    resize any image_obj while maintaining aspect ratio 
    ''' 
    orig = storage.open(image_path, 'r') 
    im = Image.open(orig, mode='r') 
    new_y = (float(dim_x) * float(im.height))/float(im.width) 
    new_im = im.resize((dim_x, int(new_y)), Image.ANTIALIAS) 
    img_path, img_name = path.split(image_path) 
    file_name, img_ext = img_name.rsplit('.', 1) 
    new_img_path = path.join(img_path, file_name + append_str + '.' + img_ext) 
    try: 
     new_f = storage.open(new_img_path, 'w') 
    except IOError as e: 
     logger.critical("Caught IOError in {}, {}".format(__file__, e)) 
     ravenclient.captureException() 
     return None 
    try: 
     new_im.save(new_f) 
    except IOError as e: 
     logger.critical("Caught IOError in {}, {}".format(__file__, e)) 
     ravenclient.captureException() 
     return None 
    except Exception as e: 
     logger.critical("Caught unhandled exception in {}. {}".format(
     __file__, e) 
     ) 
     ravenclient.captureException() 
     return None 
    im.close() 
    new_im.close() 
    new_f.close() 
    return new_img_path 

Która jest wywoływana z obsługi sygnału post_save:

@receiver(post_save, sender=Profile, dispatch_uid='resize_profile_image') 
def resize_profile_image(sender, instance=None, created=False, **kwargs): 
    if created: 
     if instance.profile_image: 
      width, height = image_dimensions(instance.profile_image.name) 
      print(width, height) 
      if width > MAX_WIDTH: 
       result = resize_image.delay(instance.profile_image.name, MAX_WIDTH) 
       instance.profile_image.name = result.get() 
      if width > THUMB_WIDTH: 
       result = resize_image.delay(
        instance.profile_image.name, 
        THUMB_WIDTH, 
        append_str='_thumb' 
       ) 
       instance.profile_image_thumb.name = result.get() 
      try: 
       instance.save() 
      except Exception as e: 
       log.critical("Unhandled exception in {}, {}".format(__name__, e)) 
       ravenclient.captureException() 

Zamiarem jest, aby wziąć przesłanych zdjęć i zmieniać ich rozmiar 1) do maksymalnej szerokości że mobilne urządzenie może wyświetlać i 2) na miniaturze 50 pikseli dla używać w aplikacji mobilnej.

Gdy patrzę na S3, nie widzę obrazów o zmienionym rozmiarze ani miniatur. Jednak testy jednostkowe (które są dokładne) nie dają żadnych błędów.

Kiedy uzyskać wymiary obrazu:

def image_dimensions(image_path): 
    f = storage.open(image_path, 'r') 
    im = Image.open(f, 'r') 
    height = im.height 
    width = im.width 
    im.close() 
    f.close() 
    return (width, height) 

Nie ma problemu dostępu ImageField obiektu. Nie otrzymuję błędu, gdy używam default_storage do otwierania obrazu profile_image instancji. Metoda PIL:

new_im = im.resize((dim_x, int(new_y)), Image.ANTIALIAS) zwraca nową instancję klasy "PIL.Image.Image".

W rzeczywistości (przepraszam mój gadatliwość)

nie zgłosi błąd:

>>> u = User(email="[email protected]", password="sdfbskjfskjfskjdf") 
>>> u.save() 
>>> p = Profile(user=u, profile_image=create_image_file()) 
>>> p.save() 
>>> from django.core.files.storage import default_storage as storage 
>>> orig = storage.open(p.profile_image.name, 'r') 
>>> orig 
<S3BotoStorageFile: uploads/profile_img/b0fd4f00-cce6-4dd3-b514-4c46a801ab19.jpg> 
>>> im = Image.open(orig, mode='r') 
>>> im 
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=5000x5000 at 0x10B8F1FD0> 
>>> im.__class__ 
<class 'PIL.JpegImagePlugin.JpegImageFile'> 
>>> dim_x = 500 
>>> new_y = (float(dim_x) * float(im.height))/float(im.width) 
>>> new_im = im.resize((dim_x, int(new_y)), Image.ANTIALIAS) 
>>> new_im.__class__ 
<class 'PIL.Image.Image'> 
>>> img_path, img_name = path.split(p.profile_image.name) 
>>> file_name, img_ext = img_name.rsplit('.', 1) 
>>> append_str='_resized' 
>>> new_img_path = path.join(img_path, file_name + append_str + '.' + img_ext) 
>>> new_f = storage.open(new_img_path, 'w') 
>>> new_f 
<S3BotoStorageFile: uploads/profile_img/b0fd4f00-cce6-4dd3-b514-4c46a801ab19_resized.jpg> 
>>> new_im.save(new_f) #### This does NOT create an S3 file!!!! 
>>> im.close() 
>>> new_im.close() 
>>> new_f.close() 

>>> p.save() przesyła nowy profil obrazu do S3. Spodziewałem się, że >>> new_im.save(new_f) zapisze plik obrazu w S3. Ale tak nie jest.

Każdy wgląd lub pomoc jest bardzo doceniany i dziękuję za poświęcenie czasu na przyjrzenie się temu problemowi.

Edit ...

Moje ustawienia:

AWS_STORAGE_BUCKET_NAME = 'testthis' 
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME 
MEDIAFILES_LOCATION = 'media' 
MEDIA_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, MEDIAFILES_LOCATION) 
DEFAULT_FILE_STORAGE = 'custom_storages.MediaStorage' 

Gdzie custom_storage.py jest

from django.conf import settings 
from storages.backends.s3boto import S3BotoStorage 

class MediaStorage(S3BotoStorage): 
    location = settings.MEDIAFILES_LOCATION 
    bucket_name = settings.AWS_STORAGE_BUCKET_NAME 
+0

Mają nowy problem: AttributeError: obiekt 'S3BotoStorageFile' ma atrybut '' startswith Zmieniono: orig = default_storage.open (image_path, 'r') im = Image.open (orig) Teraz pojawia się błąd Task common.image_lib.resize_image [48340adf-f3aa-4227-ba24-1917d4e703ef] podniesiony nieoczekiwanie: AttributeError (obiekt "S3BotoStorageFile" nie ma atrybutu 'startswith'”) – fiacre

+0

ja testowałem kodu z plików PNG, i to działa: >>> new_im.save (new_f, 'PNG') rzeczywistości tworzy nowy plik na S3 But Odpowiedni nie działa z JPEG! Czy może to być błąd PIL? – fiacre

Odpowiedz

0

Problem wydaje się być związane z PIL za JPEG Biblioteka:

>>> u = User(email="[email protected]", password="sdfbskjfskjfskjdf") 
>>> u.save() 
>>> p = Profile(user=u, profile_image=create_image_file()) 
>>> p.save() 
>>> from django.core.files.storage import default_storage as storage 
>>> orig = storage.open(p.profile_image.name, 'r') 
>>> orig 
<S3BotoStorageFile: uploads/profile_img/b0fd4f00-cce6-4dd3-b514-4c46a801ab19.png> 
>>> im = Image.open(orig, mode='r') 
>>> im.__class__ 
<class 'PIL.PngImagePlugin.PngImageFile'> 
>>> dim_x = 500 
>>> new_y = (float(dim_x) * float(im.height))/float(im.width) 
>>> new_im = im.resize((dim_x, int(new_y)), Image.ANTIALIAS) 
>>> new_im.__class__ 
<class 'PIL.Image.Image'> 
>>> img_path, img_name = path.split(p.profile_image.name) 
>>> file_name, img_ext = img_name.rsplit('.', 1) 
>>> append_str='_resized' 
>>> new_img_path = path.join(img_path, file_name + append_str + '.' + img_ext) 
>>> new_f = storage.open(new_img_path, 'w') 
>>> new_f 
<S3BotoStorageFile: uploads/profile_img/b0fd4f00-cce6-4dd3-b514-4c46a801ab19_resized.png> 
>>> new_im.save(new_f) #### This does create a file on S3! 
>>> im.close() 
>>> new_im.close() 
>>> new_f.close()