以编程方式将图像保存到Django ImageField


203

好的,我已经尝试了几乎所有内容,但无法正常工作。

  • 我有一个上面带有ImageField的Django模型
  • 我有通过HTTP下载图像的代码(已测试并且可以工作)
  • 图像直接保存到“ upload_to”文件夹中(upload_to是在ImageField上设置的文件夹)
  • 我需要做的就是将已经存在的图像文件路径与ImageField相关联

我已经用6种不同的方式编写了这段代码。

我遇到的问题是我正在编写的所有代码均导致以下行为:(1)Django将创建第二个文件,(2)重命名新文件,在文件末尾添加_名称,然后(3)不会在保留基本为空的重命名文件的情况下传输任何数据。在“ upload_to”路径中剩下的是2个文件,一个是实际图像,一个是图像名称,但为空,当然ImageField路径设置为Django尝试创建的空文件。 。

如果不清楚,我将尝试说明:

## Image generation code runs.... 
/Upload
     generated_image.jpg     4kb

## Attempt to set the ImageField path...
/Upload
     generated_image.jpg     4kb
     generated_image_.jpg    0kb

ImageField.Path = /Upload/generated_image_.jpg

如何在不让Django尝试重新存储文件的情况下执行此操作?我真正想要的就是这种效果……

model.ImageField.path = generated_image_path

...但是那当然是行不通的。

是的,我已经经历这里的其他问题,如走了这一个,以及对Django的DOC 文件

更新 在进一步测试之后,仅当在Windows Server上的Apache下运行时,它才会执行此行为。在XP上的“ runserver”下运行时,它不会执行此行为。

我很沮丧

这是在XP上成功运行的代码...

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()

另一个伟大的Django问题。我已经做了几次尝试来解决这个问题,但没有成功。在上载目录中创建的文件已损坏,与原始文件(存储在其他位置)相比,文件的大小仅为原来的一小部分。
09年

您的更新不起作用
AmiNadimi

Answers:


166

我有一些代码可以从网络上获取图像并将其存储在模型中。重要的位是:

from django.core.files import File  # you need this somewhere
import urllib


# The following actually resides in a method of my model

result = urllib.urlretrieve(image_url) # image_url is a URL to an image

# self.photo is the ImageField
self.photo.save(
    os.path.basename(self.url),
    File(open(result[0], 'rb'))
    )

self.save()

这有点令人困惑,因为它脱离了我的模型并且脱离了上下文,但是重要的部分是:

  • 从Web提取的图像存储在upload_to文件夹中,而是由urllib.urlretrieve()作为临时文件存储,之后被丢弃。
  • ImageField.save()方法采用文件名(os.path.basename位)和django.core.files.File对象。

让我知道您是否有疑问或需要澄清。

编辑:为清楚起见,这是模型(减去任何必需的import语句):

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

2
tvon-我已经尝试过实现这种效果的方法,但是也许我会再说一遍,实际上,我的代码看起来与此非常相似。(即使没有上下文,我也可以看到它的工作原理)。
T. Stone,

2
我建议也使用url解析,以避免将url paramatar gunk附加到图像上。import urlparseos.path.basename(urlparse.urlparse(self.url).path)。感谢您的帖子,对您有所帮助。
dennmat 2011年

1
我得到django.core.exceptions.SuspiciousOperation:拒绝访问'/images/10.jpg'。
DataGreed 2012年

2
@DataGreed,您应该从模型中的upload_to定义中删除正斜杠“ /”。这是在这里解决的。
tsikov 2012年

我收到这样的错误:prohibited to prevent data loss due to unsaved related object 'stream'.
Dipak

95

如果尚未创建模型,则超级简单:

首先,将您的图片文件复制到上传路径(在以下代码段中假定为'path /')。

其次,使用类似:

class Layout(models.Model):
    image = models.ImageField('img', upload_to='path/')

layout = Layout()
layout.image = "path/image.png"
layout.save()

在django 1.4中进行了测试和工作,它可能也适用于现有模型。


10
这是正确的答案,需要更多票!!!在这里也找到了这个解决方案。
安德鲁·斯威哈特

你好 我有个问题。我正在将django-storages与Amazon S3后端一起使用。这会触发新的上传吗?
Salvatore Iovene

OP询问“没有让Django尝试重新存储文件”,这就是答案!
2014年

2
Django有一些现有的逻辑来解决磁盘上重复的文件名。该方法掩盖了这种逻辑,因为让用户来检查文件名重复。
克里斯·康兰

1
@Conlan:将guid附加到文件名。
拉比·科迪

41

只是一点点。tvon答案有效,但是,如果您在Windows上工作,则可能需要open()使用'rb'。像这样:

class CachedImage(models.Model):
    url = models.CharField(max_length=255, unique=True)
    photo = models.ImageField(upload_to=photo_path, blank=True)

    def cache(self):
        """Store image locally if we have a URL"""

        if self.url and not self.photo:
            result = urllib.urlretrieve(self.url)
            self.photo.save(
                    os.path.basename(self.url),
                    File(open(result[0], 'rb'))
                    )
            self.save()

否则您的文件将在第一个0x1A字节处被截断。


1
谢谢,我倾向于忘记窗口所面对的这些低级细节。
mike_k 2011年

fml ...在Linux机器上传递该参数时会发生什么?
DMac the Destroyer 2011年

1
回答了我自己的问题...对垃圾邮件感到抱歉。在这里找到了一些文档。“在Unix上,将'b'附加到该模式并没有什么坏处,因此您可以独立于平台使用它来处理所有二进制文件。”
DMac the Destroyer 2011年

16

这是一种效果很好的方法,它还允许您将文件转换为某种格式(以避免“无法将模式P编写为JPEG”错误):

import urllib2
from django.core.files.base import ContentFile
from PIL import Image
from StringIO import StringIO

def download_image(name, image, url):
    input_file = StringIO(urllib2.urlopen(url).read())
    output_file = StringIO()
    img = Image.open(input_file)
    if img.mode != "RGB":
        img = img.convert("RGB")
    img.save(output_file, "JPEG")
    image.save(name+".jpg", ContentFile(output_file.getvalue()), save=False)

其中image是django ImageField或your_model_instance.image,这里是一个用法示例:

p = ProfilePhoto(user=user)
download_image(str(user.id), p.image, image_url)
p.save()

希望这可以帮助


12

好的,如果您需要做的只是将现有图像文件路径与ImageField相关联,那么此解决方案可能会有所帮助:

from django.core.files.base import ContentFile

with open('/path/to/already/existing/file') as f:
  data = f.read()

# obj.image is the ImageField
obj.image.save('imgfilename.jpg', ContentFile(data))

好吧,如果认真的话,已经存在的图像文件将不会与ImageField关联,但是该文件的副本将在upload_to dir中创建为“ imgfilename.jpg”,并将与ImageField关联。


2
您不应该以二进制文件形式打开它吗?
Mariusz Jamro '02

就像@MariuszJamro所说的,应该是这样的:with open('/path/to/already/existing/file', 'rb') as f:
rahmatns

也不要忘记保存对象:obj.save()
rahmatns

11

我所做的是创建自己的存储,该存储不会将文件保存到磁盘:

from django.core.files.storage import FileSystemStorage

class CustomStorage(FileSystemStorage):

    def _open(self, name, mode='rb'):
        return File(open(self.path(name), mode))

    def _save(self, name, content):
        # here, you should implement how the file is to be saved
        # like on other machines or something, and return the name of the file.
        # In our case, we just return the name, and disable any kind of save
        return name

    def get_available_name(self, name):
        return name

然后,在我的模型中,对于我的ImageField,我使用了新的自定义存储:

from custom_storage import CustomStorage

custom_store = CustomStorage()

class Image(models.Model):
    thumb = models.ImageField(storage=custom_store, upload_to='/some/path')

7

如果您只想“设置”实际的文件名,而又不会导致加载和重新保存文件(!!)或使用charfield(!!!)的开销,那么您可能想尝试这样的方法- --

model_instance.myfile = model_instance.myfile.field.attr_class(model_instance, model_instance.myfile.field, 'my-filename.jpg')

它将照亮您的model_instance.myfile.url及其所有其他内容,就像您实际上已上传文件一样。

就像@ t-stone所说的,我们真正想要的是能够设置instance.myfile.path ='my-filename.jpg',但是Django目前不支持。


如果model_instance是包含文件的模型的实例。.另一个“实例”代表什么?
h3。

7

我认为这是最简单的解决方案:

from django.core.files import File

with open('path_to_file', 'r') as f:   # use 'rb' mode for python3
    data = File(f)
    model.image.save('filename', data, True)

3

这些答案很多都已经过时了,我花了很多时间感到沮丧(对于Django和Web开发人员来说,我一般都是新手)。但是,我通过@iambibhas找到了这个出色的要点:https ://gist.github.com/iambibhas/5051911

import requests

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile


def save_image_from_url(model, url):
    r = requests.get(url)

    img_temp = NamedTemporaryFile(delete=True)
    img_temp.write(r.content)
    img_temp.flush()

    model.image.save("image.jpg", File(img_temp), save=True)

2

这可能不是您要寻找的答案。但是您可以使用charfield而不是ImageFile来存储文件的路径。这样,您可以以编程方式将上载的图像与字段相关联,而无需重新创建文件。


是的,我很想放弃这一点,要么直接写MySQL,要么只使用CharField()。
T. Stone,

1

你可以试试:

model.ImageField.path = os.path.join('/Upload', generated_image_path)

1
class tweet_photos(models.Model):
upload_path='absolute path'
image=models.ImageField(upload_to=upload_path)
image_url = models.URLField(null=True, blank=True)
def save(self, *args, **kwargs):
    if self.image_url:
        import urllib, os
        from urlparse import urlparse
        file_save_dir = self.upload_path
        filename = urlparse(self.image_url).path.split('/')[-1]
        urllib.urlretrieve(self.image_url, os.path.join(file_save_dir, filename))
        self.image = os.path.join(file_save_dir, filename)
        self.image_url = ''
    super(tweet_photos, self).save()

1
class Pin(models.Model):
    """Pin Class"""
    image_link = models.CharField(max_length=255, null=True, blank=True)
    image = models.ImageField(upload_to='images/', blank=True)
    title = models.CharField(max_length=255, null=True, blank=True)
    source_name = models.CharField(max_length=255, null=True, blank=True)
    source_link = models.CharField(max_length=255, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    tags = models.ForeignKey(Tag, blank=True, null=True)

    def __unicode__(self):
        """Unicode class."""
        return unicode(self.image_link)

    def save(self, *args, **kwargs):
        """Store image locally if we have a URL"""
        if self.image_link and not self.image:
            result = urllib.urlretrieve(self.image_link)
            self.image.save(os.path.basename(self.image_link), File(open(result[0], 'r')))
            self.save()
            super(Pin, self).save()

1

加工!您可以使用FileSystemStorage保存图像。检查下面的例子

def upload_pic(request):
if request.method == 'POST' and request.FILES['photo']:
    photo = request.FILES['photo']
    name = request.FILES['photo'].name
    fs = FileSystemStorage()
##### you can update file saving location too by adding line below #####
    fs.base_location = fs.base_location+'/company_coverphotos'
##################
    filename = fs.save(name, photo)
    uploaded_file_url = fs.url(filename)+'/company_coverphotos'
    Profile.objects.filter(user=request.user).update(photo=photo)

谢谢Nids,绝对使用这个解决方案!您节省了我很多时间:)
Mehmet Burak Ibis

0

您可以使用Django REST框架和python Requests库以编程方式将图像保存到Django ImageField

这是一个例子:

import requests


def upload_image():
    # PATH TO DJANGO REST API
    url = "http://127.0.0.1:8080/api/gallery/"

    # MODEL FIELDS DATA
    data = {'first_name': "Rajiv", 'last_name': "Sharma"}

    #  UPLOAD FILES THROUGH REST API
    photo = open('/path/to/photo'), 'rb')
    resume = open('/path/to/resume'), 'rb')
    files = {'photo': photo, 'resume': resume}

    request = requests.post(url, data=data, files=files)
    print(request.status_code, request.reason) 

0

在Django 3中,具有这样的模型:

class Item(models.Model):
   name = models.CharField(max_length=255, unique=True)
   photo= models.ImageField(upload_to='image_folder/', blank=True)

如果图片已经上传,我们可以直接做:

Item.objects.filter(...).update(photo='image_folder/sample_photo.png')

要么

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.