Python Django:使用PIL、AmazonS3和Boto调整图像大小并上传

Python Django:使用PIL、AmazonS3和Boto调整图像大小并上传,python,django,amazon-s3,python-imaging-library,boto,Python,Django,Amazon S3,Python Imaging Library,Boto,我正试图找出最好的方法来获取用户上传的图像,调整其大小,并将原始图像和调整大小的图像存储在AmazonS3上 我正在运行Django 1.5,使用PIL调整图像大小,并使用Boto将图像文件上载到S3。现在我已经将原始图像上传到S3,使用PIL使用S3路径打开图像并调整大小,然后将调整大小的版本保存到S3,但是这似乎不是最有效的方法 我想知道是否有一种方法可以在上传到S3之前使用用户上传的图像本身调整图像大小(在让PIL打开图像文件本身时遇到问题),以及这是否会比我现在设置的方式更快。我似乎在P

我正试图找出最好的方法来获取用户上传的图像,调整其大小,并将原始图像和调整大小的图像存储在AmazonS3上

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

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

有没有比我目前设置的更有效的方法?最好能对每一步发生的事情以及为什么以这种方式进行设置最有意义做出一般性的解释


我还应该提到,将图像上传到S3的时间似乎比我在服务器上存储图像的时间要长得多。上传到S3时是否存在正常延迟,或者在设置方面是否存在可能会减慢S3上传速度的潜在因素?

我有一个由Heroku中的Django+Tastypie和S3中的image wharehouse组成的体系结构。当用户从前端上传照片(用JS编写)时,我要做的是将照片大小调整为一定大小(最大大小为600 x 600),始终保持纵横比。我将粘贴代码来执行此操作(它可以工作)

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)
在这里,我要做的是检查图像是否大于280 x 281(前端的裁剪正方形具有该大小),并检查图像的一侧是否大于600px。如果是这样,我调用原始类的(自定义)方法resize

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
您最不需要的是包含自定义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)

您的设置文件中应该有AWS\u访问\u密钥\u ID、AWS\u机密\u访问\u密钥、AWS\u存储\u BUCKET\u名称、S3\u URL。

上传时是否在AWS实例上运行?我发现在aws网络之外移动文件时会有轻微的延迟。@sean谢谢你提供的信息。我不是在aws疯狂上运行,所以很高兴知道。设置一个微型ec2实例是不可能的吗?你可以把图像发送到那里,进行转换,然后把它们放到S3中?当您无法在本地打开文件以调整大小然后上载到S3时,是否也会发生错误?Thank@sean将研究设置micro ec2实例的可能性,并了解其工作原理。仍在通过亚马逊的网络服务学习我的方法。