一个类似StringIO的类,扩展django.core.files.File

5 投票
3 回答
7846 浏览
提问于 2025-04-16 01:49
class MyModel(models.Model)
 image = models.FileField(upload_to="blagh blagh...")
 #more spam...

我在内存中有一个文件,我想通过Django的FileField保存这个文件,像这样:

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

我试着使用StringIO,但它没有继承django.core.files.File,所以没有实现chunks()这个方法。我是这样把它包装成一个File对象的:

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

但是File的方法使用的是提供文件的大小和名称字段,而StringIO并没有定义这些字段。我找到过这个链接,但现在链接已经失效了。

3 个回答

8

关于Jason的回答,注意到ContentFile只接受字符串,而不接受任何类似文件的对象。这里有一个可以接受的例子 --

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()

现在你可以像这样操作 --

photo.image.save(name, StreamFile(io))
34

你可以用 ContentFile 来代替 File。

from django.core.files.base import ContentFile

photo.image.save(name, ContentFile(buffer))
8

如果你有一串字节流,想把它保存到一个文件字段或者图片字段,这里有段代码可能会对你有帮助:

>>> 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: ...>

一些小提示:

  • 你可以随意给图片起个名字,但最好保持文件扩展名的正确性,比如.jpg或.png。
  • InMemoryUploadedFile的第二个参数中,填入你模型里字段的名字,这里是“image”。

这个过程有点麻烦,但最终能完成任务。希望在1.3/4版本中,API能变得更简洁一些。

补充:
可以查看Jason的回答,那里面有更简单的方法,不过你还是需要知道图片的文件名。

撰写回答