5
class MyModel(models.Model)
 image = models.FileField(upload_to="blagh blagh...")
 #more spam...

I have a file in memory and I want to save it via Django FileField save method, like this:

photo.image.save(name, buffer) # second arg should be django File

I've tried to use StringIO, but it doesn't extend django.core.files.File and thus doesn't implement method chunks(). I've wrapped it in a File object like that:

buffile = File(buffer, name) # first argument should be a file
photo.image.save(name, buffile)

But File methods use size and name fields of supplied file. StringIO doesn't define them. I've found this, but the link is dead

Community
  • 1
  • 1
joozek
  • 2,143
  • 2
  • 21
  • 32
  • Use the [Image](http://www.pythonware.com/library/pil/handbook/introduction.htm) class. – joel3000 Jul 25 '10 at 20:16
  • I considered that, but then I would not use django mechanism for naming files (if a file with a given name exists, add underscore to the filename). And yes, I know, I could do that by myself and I could do that in other way, but It would be the best if I did that, like I described before. – joozek Jul 25 '10 at 21:25

3 Answers3

34

You can use ContentFile instead of File

from django.core.files.base import ContentFile

photo.image.save(name, ContentFile(buffer))
Jason
  • 531
  • 3
  • 2
8

If you've got a stream of bytes, which you'd like to save to a FileField/ImageField, here's some code that might help:

>>> from django.core.files.uploadedfile import InMemoryUploadedFile
>>> from cStringIO import StringIO
>>> buf = StringIO(data)  # `data` is your stream of bytes
>>> buf.seek(0, 2)  # Seek to the end of the stream, so we can get its length with `buf.tell()`
>>> file = InMemoryUploadedFile(buf, "image", "some_filename.png", None, buf.tell(), None)
>>> photo.image.save(file.name, file)  # `photo` is an instance of `MyModel`
>>> photo.image
<ImageFieldFile: ...>

Some notes:

  • You can make up whatever name you want for the image, but you'll probably want to keep the extension accurate
  • The second argument to InMemoryUploadedFile is the name of the field in your model, hence "image"

It's a little finnicky, but it gets the job done. Hopefully, the API will get cleaned up a bit more in 1.3/4.

Edit:
See Jason's answer for a much simpler way of doing this, though you'll still want to know the filename of the image.

Community
  • 1
  • 1
elo80ka
  • 14,837
  • 3
  • 36
  • 43
8

Re Jason's answer. Note that ContentFile only accepts strings, not any file-like object. Here's one that does --

from django.core.files.base import *

class StreamFile(ContentFile):
    """
    Django doesn't provide a File wrapper suitable 
    for file-like objects (eg StringIO)
    """
    def __init__(self, stream):
        super(ContentFile, self).__init__(stream)
        stream.seek(0, 2)
        self.size = stream.tell()

Now you can do stuff like this --

photo.image.save(name, StreamFile(io))
s29
  • 2,027
  • 25
  • 20