Which one I have to use to read image in Django, StringIO or BytesIO?
<p>I'm trying to compress the image file before uploading in my <code>django</code> application.</p>

<p>I found nice code snippet site : <a href="https://djangosnippets.org/snippets/10460/" rel="nofollow">https://djangosnippets.org/snippets/10460/</a></p>

<p>but It doesn't work in <code>python3</code>. I think the problem is about <code>str</code> or <code>byte</code>.</p>

<p>Someone advise to use <code>BytesIO</code> instead of <code>StringIO</code>.</p>

<p>So, I edit my code like this.</p>

<pre><code>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)
</code></pre>

<p>But it occurs an error : <code>'_io.BytesIO' object has no attribute 'len'</code> --> <code>output.len</code> in my code occurs an error.</p>

<p>I start to doubt that It is right way to use <code>BytesIO</code> instead of <code>StringIO</code>. </p>

<p>And need some helps how to edit my code, too. Thanks.</p>

<p>I modified the code to use the <code>with</code> statement so there is no need to close the files yourself.</p>

<pre><code>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)
</code></pre>

python django python-imaging-library