Skip to main content
 首页 » 编程设计

python之Django:使用 PIL、Amazon S3 和 Boto 调整图像大小和上传

2025年05月04日35jiqing9006

我正在尝试找出最佳方法来获取用户上传的图像、调整其大小并将原始图像以及调整后的图像存储在 Amazon S3 上。

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

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

有没有比我目前设置的方法更有效的方法?对每一步发生的事情以及为什么以这种方式设置最有意义的一般解释是理想的。

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

请您参考如下方法:

我的架构由 Heroku 中的 Django + Tastypie 和 S3 中的图像仓库组成。当用户从前端(用 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(前端的裁剪方 block 具有该尺寸),并检查图像的一侧是否大于 600 像素。如果是这种情况,我将调用我的原始类的(自定义)方法调整大小...

模型.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_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_STORAGE_BUCKET_NAME、S3_URL。