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

好吧,我几乎什么都试过了,但我不能让它工作。

  • 我有一个带有ImageField的Django模型
  • 我有通过HTTP下载图像的代码(测试和工作)
  • 图像直接保存到'upload_to'文件夹中(upload_to是在ImageField中设置的)
  • 我所需要做的就是将已经存在的图像文件路径与ImageField关联起来

我用六种不同的方式写了这段代码。

我遇到的问题是所有的代码,我写的结果如下行为: (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文档

<强>更新 经过进一步的测试,它只有在Windows Server上的Apache下运行时才会执行此行为。当在XP的“runserver”下运行时,它不会执行此行为。< / p >

我被难住了。

下面是在XP上成功运行的代码…

f = open(thumb_path, 'r')
model.thumbnail = File(f)
model.save()
181255 次浏览

这可能不是你想要的答案。但是您可以使用charfield来存储文件的路径,而不是ImageFile。通过这种方式,您可以通过编程方式将上传的图像关联到字段,而无需重新创建文件。

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

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()存储为tempfile,随后被丢弃。
  • 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()

就说一点。这个答案是可行的,但是,如果你在windows上工作,你可能想用'rb' open()文件。是这样的:

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字节被截断。

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

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

如果您只想“设置”实际的文件名,而不需要加载和重新保存文件(!!),或者使用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.ImageField.path = os.path.join('/Upload', generated_image_path)

下面是一个工作良好的方法,允许您将文件转换为某种格式(以避免“不能将模式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()

希望这能有所帮助

如果模型还没有创建,超级简单:

第一个,复制你的图像文件到上传路径(假设在下面的片段= “路径/”)。

第二个,使用像这样的东西:

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


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

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

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

好的,如果你所需要做的只是将已经存在的图像文件路径与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相关联。

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

另一种可能的方式是:

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)

你可以使用Django REST框架和python 请求库以编程方式将图像保存到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)
< p >工作! 您可以使用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)

这些答案很多都过时了,我花了很多时间在沮丧中(我对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)


< span style > < span style > 使用如下的模型:

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()
class DemoImage(models.Model):
title = models.TextField(max_length=255, blank=False)
image = models.ImageField(blank=False, upload_to="images/DemoImages/")


import requests
import urllib.request
from django.core.files import File
url = "https://path/to/logo.jpg"


# Below 3 lines is to fake as browser agent
# as many sites block urllib class suspecting to be bots
opener = urllib.request.build_opener()
opener.addheaders = [("User-agent", "Mozilla/5.0")]
urllib.request.install_opener(opener)


# Issue command to actually download and create temp img file in memory
result = urllib.request.urlretrieve(url)


# DemoImage.objects.create(title="title", image=File(open(result[0], "rb")))
# ^^ This erroneously results in creating the file like
# images/DemoImages/path/to/temp/dir/logo_image_file
# as opposed to
# images/DemoImages/logo_image_file


# Solution to get the file in images/DemoImages/
reopen = open(result[0], "rb") # Returns a BufferedReader object of the temp image
django_file = File(reopen)     # Create the file from the BufferedReader object
demoimg = DemoImage()
demoimg.title = "title"
demoimg.image.save("logo.png", django_file, save=True)

如果这样配置,这种方法还会触发文件上传到cloudinary/S3

所以,如果你有一个imagefield和upload_to属性集的模型,比如:

class Avatar(models.Model):
image_file = models.ImageField(upload_to=user_directory_path_avatar)

至少在django 3.15中,可以很容易地改变图像。

在视图中,对图像进行处理时,可以从以下路径获取图像:

self.request.FILES['avatar']

这是类型InMemoryUploadedFile的一个实例,只要你的html表单有enctype集和一个字段的头像…

    <form method="post" class="avatarform" id="avatarform" action="{% url avatar_update_view' %}" enctype="multipart/form-data">
{% csrf_token %}
<input id="avatarUpload" class="d-none" type="file" name="avatar">
</form>

然后,在视图中设置新图像非常简单,如下所示(其中profile是self.request.user的概要模型)

profile.avatar.image_file.save(self.request.FILES['avatar'].name, self.request.FILES['avatar'])

不需要保存配置文件。头像,image_field已经保存,并到正确的位置,因为'upload_to'回调函数。

我用uuid在django 2 python 3中保存图像,因为这是django如何做的:

import uuid
from django.core.files import File
import urllib


httpUrl = "https://miimgeurl/image.jpg"
result = urllib.request.urlretrieve(httpUrl)
mymodel.imagefield.save(os.path.basename(str(uuid.uuid4())+".jpg"),File(open(result[0], 'rb')))
mymodel.save()

如果你使用admin.py你可以解决这个问题override (姜戈医生):

def save_model(self, request, obj, form, change):
obj.image_data = bytes(obj.image_name.read())
super().save_model(request, obj, form, change)

models.py:

image_name = models.ImageField()
image_data = models.BinaryField()