Python/Django 从URL下载图像,修改并保存到ImageField

10 投票
1 回答
10634 浏览
提问于 2025-04-16 02:34

我一直在寻找一种方法,可以从一个网址下载图片,对图片进行一些处理(比如调整大小),然后把它保存到Django的ImageField里。通过下面提到的两篇很棒的文章,我已经能够下载并保存图片到ImageField了。不过,在拿到文件后,我在处理它时遇到了一些麻烦。

具体来说,模型的save()方法需要一个File()对象作为第二个参数。所以我的数据最终必须是一个File()对象。下面的博客文章展示了如何使用urllib2把图片网址保存为File()对象。这很好,但我还想用PIL把图片处理成Image()对象(或者ImageFile对象)。

我更希望的做法是直接把图片网址加载到一个Image()对象中,进行调整大小的操作,然后转换成File()对象,最后保存到模型里。不过,我尝试把Image()转换成File()时失败了。如果可能的话,我想尽量减少写入磁盘的次数,所以我希望能在内存中完成这个对象的转换,或者使用一个NamedTemporaryFile(delete=True)对象,这样就不用担心多余的文件留在那儿。(当然,我希望文件在通过模型保存后能写入磁盘)。

import urllib2
from PIL import Image, ImageFile    
from django.core.files import File
from django.core.files.temp import NamedTemporaryFile

inStream = urllib2.urlopen('http://www.google.com/intl/en_ALL/images/srpr/logo1w.png')

parser = ImageFile.Parser()
while True:
    s = inStream.read(1024)
    if not s:
        break
    parser.feed(s)

inImage = parser.close()
# convert to RGB to avoid error with png and tiffs
if inImage.mode != "RGB":
    inImage = inImage.convert("RGB")

# resize could occur here

# START OF CODE THAT DOES NOT SEEM TO WORK
# I need to somehow convert an image .....

img_temp = NamedTemporaryFile(delete=True)
img_temp.write(inImage.tostring())
img_temp.flush()

file_object = File(img_temp)

# .... into a file that the Django object will accept. 
# END OF CODE THAT DOES NOT SEEM TO WORK

my_model_instance.image.save(
         'some_filename',
         file_object,  # this must be a File() object
         save=True,
         )

用这种方法时,文件在我查看时总是显示损坏。有没有人有办法可以从网址获取文件,允许我把它作为Image处理,然后再保存到Django的ImageField里?

任何帮助都非常感谢。

通过编程将图片保存到Django的ImageField

Django:从图片网址添加图片到ImageField

更新 08/11/2010:我最终选择了StringIO,不过,当我尝试把它保存到Django的ImageField时,StringIO抛出了一个异常。具体来说,堆栈跟踪显示了一个名称错误:

"AttribueError exception "StringIO instance has no attribute 'name'"

经过查阅Django的源代码,发现这个错误是因为模型的save方法试图访问StringIO“文件”的大小属性时引起的。(虽然上面的错误提示表明是名称的问题,但这个错误的根本原因似乎是StringIO图像缺少大小属性)。一旦我给图像文件的大小属性赋值,它就正常工作了。

1 个回答

5

为了实现一举两得,为什么不使用(c)StringIO对象来代替NamedTemporaryFile呢?这样你就不需要把文件存储在硬盘上了。我知道像这样的做法是可行的(我自己也用过类似的代码)。

from cStringIO import StringIO
img_temp = StringIO()
inImage.save(img_temp, 'PNG')
img_temp.seek(0)

file_object = File(img_temp, filename)

撰写回答