在Django中读取图像时,我该使用StringIO还是BytesIO?

5
我正在尝试在我的django应用程序中上传图像文件之前对其进行压缩。我在一个很好的代码片段网站上找到了这个代码: https://djangosnippets.org/snippets/10460/,但是它在python3中无法运行。我认为问题可能与strbyte有关。有人建议使用BytesIO代替StringIO。所以,我将我的代码编辑成这样。
from django.db import models
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.utils.text import slugify
from django.core.files.uploadedfile import InMemoryUploadedFile

from PIL import Image as Img
from io import StringIO, BytesIO

def upload_location(instance, file_name):
    return "{}/{}/{}/{}".format(
        "album",
        instance.day,
        instance.week,
        file_name
    )


class Album(models.Model):

    DAYS = (
        ('Sun', '일요일'),
        ('Mon', '월요일'),
    )
    name = models.CharField(max_length=50)
    description = models.CharField(max_length=100, blank=True)
    image = models.ImageField(upload_to=upload_location)
    day = models.CharField(max_length=3, choices=DAYS)
    week = models.IntegerField()
    slug = models.SlugField(unique=True, allow_unicode=True)
    date = models.DateField()

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['day', 'week']

    def __str__(self):
        return "{} - {}주차".format(self.get_day_display(), self.week)

    def get_absolute_url(self):
        return reverse(
            "album:album_detail",
            kwargs={
                "slug": self.slug
            }
        )

    def save(self, *args, **kwargs):
        if not self.id:
            self.slug = self.slug + "주차"

        if self.image:
            img = Img.open(BytesIO(self.image.read()))
            if img.mode != 'RGB':
                img = img.convert('RGB')
            img.thumbnail((self.image.width/1.5,self.image.height/1.5), Img.ANTIALIAS)
            output = BytesIO()
            img.save(output, format='JPEG', quality=70)
            output.seek(0)
            self.image= InMemoryUploadedFile(
                output,'ImageField',
                "%s.jpg" %self.image.name.split('.')[0],
                'image/jpeg',
                output.len, None
            )
        super().save(*args, **kwargs)

但是出现了一个错误:'_io.BytesIO'对象没有属性'len' --> 我的代码中的output.len出现了错误。

我开始怀疑使用BytesIO而不是StringIO的方式是否正确。

同时需要一些帮助来编辑我的代码,谢谢。


你可以通过先获取其缓冲区来获得BytesIO对象数据的长度。然后,你可以使用len(buffer)来获取其长度。此外,你还需要使用buf.release()释放缓冲区view,以便关闭BytesIO对象(出于某种原因,你似乎没有这样做)。 - Sevanteri
虽然您可能需要整个BytesIO对象的大小而不仅仅是缓冲区的大小。您可以使用sys.getsizeof来获取它。 - Sevanteri
那么,我该如何修复我的代码? - user3595632
1个回答

3
我修改了代码,使用with语句,这样就不需要手动关闭文件了。
def save(self, *args, **kwargs):
    if not self.id:
        self.slug = self.slug + "주차"

    if self.image:
        with Img.open(BytesIO(self.image.read())) as img:
            if img.mode != 'RGB':
                img = img.convert('RGB')

            img.thumbnail((self.image.width/1.5,self.image.height/1.5), Img.ANTIALIAS)
            with BytesIO() as output:
                img.save(output, format='JPEG', quality=70)
                output.seek(0)
                self.image = InMemoryUploadedFile(
                    output,'ImageField',
                    "%s.jpg" %self.image.name.split('.')[0],
                    'image/jpeg',
                    output.getbuffer().nbytes, None
                )

    super().save(*args, **kwargs)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接