Django:图像大小调整,并上传了PIL, Amazon S3和Boto。

时间:2021-07-11 00:26:03

I'm trying to figure out the best way to take a user uploaded image, resize it, and store the original image as well as the resized image on Amazon S3.

我正在尝试找出一个最好的方法来获取用户上传的图像,调整大小,并存储原始图像以及在Amazon S3上的缩放图像。

I'm running Django 1.5, using PIL to resize the image, and using Boto to handle uploading the image file to S3. Right now I've got it to work by uploading the original image to S3, using PIL to open the image using the S3 path and resize it, and then saving the resized version to S3, however this doesn't seem to be the most efficient way to do this.

我正在运行Django 1.5,使用PIL来调整图像的大小,并使用Boto处理将图像文件上传到S3的问题。现在,我通过将原始图像上传到S3,使用PIL打开图像,使用S3路径调整大小,然后将调整大小的版本保存到S3,但是这似乎不是最有效的方法。

I'm wondering if there's a way to resize the image before uploading to S3 using the user-uploaded image itself (been having trouble getting PIL to open the image file itself), and whether this would be faster than the way I've set things up now. I can't seem to find an answer to this, either in the PIL documentation or anywhere else. I should mention that I don't want to just use a third party app to handle this, as part of my goal is to learn and understand fundamentally what is going on.

我想知道是否有一种方法可以在使用用户上传的图像本身(在让PIL打开图像文件本身时遇到麻烦)上传至S3之前调整图像的大小,以及这是否比我现在设置的速度更快。我似乎找不到答案,无论是在PIL文档还是其他地方。我要指出的是,我不想仅仅使用第三方应用程序来处理这个问题,因为我的目标之一就是从根本上了解和理解正在发生的事情。

Is there a more efficient way to do this than what I've currently set up? A general explanation of what is happening at each step and why it makes the most sense to set things up that way would be ideal.

有比我现在建立的更有效的方法吗?对每一步发生的事情的一般解释,以及为什么要以这样的方式设置事物,是最理想的。

I should also mention that it seems to take much longer to upload the image to S3 than when I was just storing the image on my server. Is there a normal lag when uploading to S3 or is there potentially something in how things are set up that could be slowing down the S3 uploads?

我还要指出的是,上传图片到S3要比我在服务器上存储图片花费的时间长得多。在上传至S3时是否存在正常的延迟,或者在设置方面是否存在可能减慢S3上传的问题?

1 个解决方案

#1


1  

I have an architecture consisting of a Django + Tastypie in Heroku and the image wharehouse in S3. What I do when a user uploads a photo from the frontend (written in JS), is resize the photo to a certain size (600 x 600 max size) always mantaining the aspect ratio. I'll paste the code to do this (it works).

我有一个建筑,由Heroku的Django + taste ypie和S3的wharehouse图像组成。当用户从前端(用JS编写)上传照片时,我所做的就是将照片的大小调整到特定的大小(600 x 600最大尺寸),始终保持高宽比。我将粘贴代码来实现这一点(它是有效的)。

views.py:

views.py:

class UploadView(FormView):
    form_class = OriginalForm
    def form_valid(self, form):
        original = form.save()
        if  original.image_width > 280 and original.image_height > 281:
            if original.image_width > 600 or original.image_height > 600:
                original.resize((600, 600))
                if not original.image:
                    return self.success(self.request, form, None, errors = 'Error while uploading the image')
                original.save()
                up = UserProfile.objects.get(user = request.user.pk)
                #Save the images to s3
                s3 = S3Custom()
                new_image = s3.upload_file(original.image.path, 'avatar')
                #Save the s3 image path, as string, in the user profile
                up.avatar = new_image
                up.save
        else:
            return self.success(self.request, form, None, errors = 'The image is too small')       
        return self.success(self.request, form, original)

Here what I do is checking if the image is larger than 280 x 281 (the crop square, in the frontend, has that size), and also check if one of the sides of the image is larger than 600px. If that's the case, I call the (custom) method resize, of my Original class...

这里我要做的是检查图像是否大于280 x 281(在前端的裁剪正方形有这个大小),并检查图像的一侧是否大于600px。如果是这种情况,我调用(自定义)方法resize,我的原始类…

models.py:

models.py:

class Original(models.Model):
    def upload_image(self, filename):
        return u'avatar/{name}.{ext}'.format(            
            name = uuid.uuid4().hex,
            ext  = os.path.splitext(filename)[1].strip('.')
        )

    def __unicode__(self):
        return unicode(self.image)

    owner = models.ForeignKey('people.UserProfile')
    image = models.ImageField(upload_to = upload_image, width_field  = 'image_width', height_field = 'image_height')
    image_width = models.PositiveIntegerField(editable = False, default = 0)
    image_height = models.PositiveIntegerField(editable = False, default = 0)  

    def resize(self, size):
        if self.image is None or self.image_width is None or self.image_height is None:
            print 'Cannot resize None things'
        else:
            IMG_TYPE = os.path.splitext(self.image.name)[1].strip('.')
            if IMG_TYPE == 'jpeg':
                PIL_TYPE = 'jpeg'
                FILE_EXTENSION = 'jpeg'
            elif IMG_TYPE == 'jpg':
                PIL_TYPE = 'jpeg'
                FILE_EXTENSION = 'jpeg'
            elif IMG_TYPE == 'png':
                PIL_TYPE = 'png'
                FILE_EXTENSION = 'png'
            elif IMG_TYPE == 'gif':
                PIL_TYPE = 'gif'
                FILE_EXTENSION = 'gif'
            else:
                print 'Not a valid format'
                self.image = None
                return
            #Open the image from the ImageField and save the path
            original_path = self.image.path
            fp = open(self.image.path, 'rb')
            im = Image.open(StringIO(fp.read()))
            #Resize the image
            im.thumbnail(size, Image.ANTIALIAS)
            #Save the image
            temp_handle = StringIO()
            im.save(temp_handle, PIL_TYPE)
            temp_handle.seek(0)
            #Save image to a SimpleUploadedFile which can be saved into ImageField
            suf = SimpleUploadedFile(os.path.split(self.image.name)[-1], temp_handle.read(), content_type=IMG_TYPE)
            #Save SimpleUploadedFile into image field
            self.image.save('%s.%s' % (os.path.splitext(suf.name)[0],FILE_EXTENSION), suf, save=False)
            #Delete the original image
            fp.close()
            os.remove(original_path)
            #Save other fields
            self.image_width = im.size[0]
            self.image_height = im.size[1]
        return

The last thing you need is a "library" containing custom s3 methods:

您最不需要的是包含自定义s3方法的“库”:

class S3Custom(object):
    conn = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
    b = Bucket(conn, settings.AWS_STORAGE_BUCKET_NAME)
    k = Key(b)
    def upload_file(self, ruta, prefix):
        try:           
            self.k.key = '%s/%s' % (prefix, os.path.split(ruta)[-1])
            self.k.set_contents_from_filename(ruta)
            self.k.make_public()
        except Exception, e:
            print e
        return '%s%s' % (settings.S3_URL, self.k.key)

You should have AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_STORAGE_BUCKET_NAME, S3_URL in your settings file.

您应该在设置文件中包含AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_STORAGE_BUCKET_NAME、S3_URL。

#1


1  

I have an architecture consisting of a Django + Tastypie in Heroku and the image wharehouse in S3. What I do when a user uploads a photo from the frontend (written in JS), is resize the photo to a certain size (600 x 600 max size) always mantaining the aspect ratio. I'll paste the code to do this (it works).

我有一个建筑,由Heroku的Django + taste ypie和S3的wharehouse图像组成。当用户从前端(用JS编写)上传照片时,我所做的就是将照片的大小调整到特定的大小(600 x 600最大尺寸),始终保持高宽比。我将粘贴代码来实现这一点(它是有效的)。

views.py:

views.py:

class UploadView(FormView):
    form_class = OriginalForm
    def form_valid(self, form):
        original = form.save()
        if  original.image_width > 280 and original.image_height > 281:
            if original.image_width > 600 or original.image_height > 600:
                original.resize((600, 600))
                if not original.image:
                    return self.success(self.request, form, None, errors = 'Error while uploading the image')
                original.save()
                up = UserProfile.objects.get(user = request.user.pk)
                #Save the images to s3
                s3 = S3Custom()
                new_image = s3.upload_file(original.image.path, 'avatar')
                #Save the s3 image path, as string, in the user profile
                up.avatar = new_image
                up.save
        else:
            return self.success(self.request, form, None, errors = 'The image is too small')       
        return self.success(self.request, form, original)

Here what I do is checking if the image is larger than 280 x 281 (the crop square, in the frontend, has that size), and also check if one of the sides of the image is larger than 600px. If that's the case, I call the (custom) method resize, of my Original class...

这里我要做的是检查图像是否大于280 x 281(在前端的裁剪正方形有这个大小),并检查图像的一侧是否大于600px。如果是这种情况,我调用(自定义)方法resize,我的原始类…

models.py:

models.py:

class Original(models.Model):
    def upload_image(self, filename):
        return u'avatar/{name}.{ext}'.format(            
            name = uuid.uuid4().hex,
            ext  = os.path.splitext(filename)[1].strip('.')
        )

    def __unicode__(self):
        return unicode(self.image)

    owner = models.ForeignKey('people.UserProfile')
    image = models.ImageField(upload_to = upload_image, width_field  = 'image_width', height_field = 'image_height')
    image_width = models.PositiveIntegerField(editable = False, default = 0)
    image_height = models.PositiveIntegerField(editable = False, default = 0)  

    def resize(self, size):
        if self.image is None or self.image_width is None or self.image_height is None:
            print 'Cannot resize None things'
        else:
            IMG_TYPE = os.path.splitext(self.image.name)[1].strip('.')
            if IMG_TYPE == 'jpeg':
                PIL_TYPE = 'jpeg'
                FILE_EXTENSION = 'jpeg'
            elif IMG_TYPE == 'jpg':
                PIL_TYPE = 'jpeg'
                FILE_EXTENSION = 'jpeg'
            elif IMG_TYPE == 'png':
                PIL_TYPE = 'png'
                FILE_EXTENSION = 'png'
            elif IMG_TYPE == 'gif':
                PIL_TYPE = 'gif'
                FILE_EXTENSION = 'gif'
            else:
                print 'Not a valid format'
                self.image = None
                return
            #Open the image from the ImageField and save the path
            original_path = self.image.path
            fp = open(self.image.path, 'rb')
            im = Image.open(StringIO(fp.read()))
            #Resize the image
            im.thumbnail(size, Image.ANTIALIAS)
            #Save the image
            temp_handle = StringIO()
            im.save(temp_handle, PIL_TYPE)
            temp_handle.seek(0)
            #Save image to a SimpleUploadedFile which can be saved into ImageField
            suf = SimpleUploadedFile(os.path.split(self.image.name)[-1], temp_handle.read(), content_type=IMG_TYPE)
            #Save SimpleUploadedFile into image field
            self.image.save('%s.%s' % (os.path.splitext(suf.name)[0],FILE_EXTENSION), suf, save=False)
            #Delete the original image
            fp.close()
            os.remove(original_path)
            #Save other fields
            self.image_width = im.size[0]
            self.image_height = im.size[1]
        return

The last thing you need is a "library" containing custom s3 methods:

您最不需要的是包含自定义s3方法的“库”:

class S3Custom(object):
    conn = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
    b = Bucket(conn, settings.AWS_STORAGE_BUCKET_NAME)
    k = Key(b)
    def upload_file(self, ruta, prefix):
        try:           
            self.k.key = '%s/%s' % (prefix, os.path.split(ruta)[-1])
            self.k.set_contents_from_filename(ruta)
            self.k.make_public()
        except Exception, e:
            print e
        return '%s%s' % (settings.S3_URL, self.k.key)

You should have AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_STORAGE_BUCKET_NAME, S3_URL in your settings file.

您应该在设置文件中包含AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_STORAGE_BUCKET_NAME、S3_URL。