简体   繁体   English

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

[英]Django: Image Resize and Upload with PIL, Amazon S3 and Boto

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. 我正在运行Django 1.5,使用PIL调整图像大小,并使用Boto处理将图像文件上传到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. 现在,我可以通过将原始图像上传到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. 我想知道是否有一种方法可以使用用户上传的图像本身将图像上载到S3之前(因为无法让PIL打开图像文件本身),并且这是否会比我设置的方法更快现在的事情。 I can't seem to find an answer to this, either in the PIL documentation or anywhere else. 在PIL文档或其他任何地方,我似乎都找不到答案。 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. 我应该提到,我不想仅使用第三方应用程序来处理此问题,因为我的目标是从根本上了解和了解正在发生的事情。

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. 我还应该提到,将图像上传到S3似乎比将图像存储在服务器上花费的时间更长。 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上传的速度?

I have an architecture consisting of a Django + Tastypie in Heroku and the image wharehouse in S3. 我有一个由Heroku中的Django + Deliciouspie和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. 当用户从前端(用JS编写)上传照片时,我要做的是将照片调整为一定尺寸(最大600 x 600最大尺寸),并始终保持宽高比。 I'll paste the code to do this (it works). 我将粘贴代码以执行此操作(它可以正常工作)。

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. 在这里,我要做的是检查图像是否大于280 x 281(前端的裁切正方形具有该尺寸),并检查图像的一面是否大于600px。 If that's the case, I call the (custom) method resize, of my Original class... 如果是这种情况,我可以调用Original类的(custom)方法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。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM