5

I'm trying to compress the image file before uploading in my django application.

I found nice code snippet site : https://djangosnippets.org/snippets/10460/

but It doesn't work in python3. I think the problem is about str or byte.

Someone advise to use BytesIO instead of StringIO.

So, I edit my code like this.

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)

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

I start to doubt that It is right way to use BytesIO instead of StringIO.

And need some helps how to edit my code, too. Thanks.

user3595632
  • 5,380
  • 10
  • 55
  • 111
  • You can get the length of the `BytesIO` object's data by [getting it's buffer first](https://docs.python.org/3/library/io.html#io.BytesIO.getbuffer). Then you can get it's length with `len(buffer)`. You also need to release the buffer `view` with `buf.release()` so that the `BytesIO` object can be closed (which you don't seem to be doing for some reason). – Sevanteri Aug 29 '16 at 05:54
  • Though you might need the whole `BytesIO` object's size instead of just the buffer's size. You can get that with [`sys.getsizeof`](http://stackoverflow.com/questions/26827055/python-how-to-get-iobytes-allocated-memory-length). – Sevanteri Aug 29 '16 at 05:58
  • So, how can I fix my code? – user3595632 Aug 29 '16 at 06:51

1 Answers1

3

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

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)
Sevanteri
  • 3,749
  • 1
  • 23
  • 27