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