Lập trình lưu hình ảnh vào Django ImageField


203

Ok, tôi đã thử gần như mọi thứ và tôi không thể làm việc này.

  • Tôi có một mô hình Django với ImageField trên đó
  • Tôi có mã tải hình ảnh qua HTTP (đã thử nghiệm và hoạt động)
  • Hình ảnh được lưu trực tiếp vào thư mục 'upload_to' (upload_to là hình ảnh được đặt trên ImageField)
  • Tất cả những gì tôi cần làm là liên kết đường dẫn tệp hình ảnh đã có với ImageField

Tôi đã viết mã này về 6 cách khác nhau.

Vấn đề tôi gặp phải là tất cả các mã mà tôi đang viết kết quả trong hành vi sau: (1) Django sẽ tạo tệp thứ 2, (2) đổi tên tệp mới, thêm _ vào cuối tệp tên, sau đó (3) không chuyển bất kỳ dữ liệu nào về cơ bản để lại một tệp được đặt tên lại trống. Những gì còn lại trong đường dẫn 'upload_to' là 2 tệp, một là hình ảnh thực tế và một là tên của hình ảnh, nhưng trống rỗng, và tất nhiên đường dẫn ImageField được đặt thành tệp trống mà Django cố gắng tạo .

Trong trường hợp không rõ ràng, tôi sẽ cố gắng minh họa:

## 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

Làm thế nào tôi có thể làm điều này mà không cần Django cố gắng lưu trữ lại tệp? Những gì tôi thực sự thích là một cái gì đó cho hiệu ứng này ...

model.ImageField.path = generated_image_path

... nhưng tất nhiên là không được.

Và vâng, tôi đã trải qua các câu hỏi khác ở đây như câu hỏi này cũng như tài liệu django trên File

CẬP NHẬT Sau khi thử nghiệm thêm, nó chỉ thực hiện hành vi này khi chạy trong Apache trên Windows Server. Trong khi chạy dưới 'máy chủ' trên XP, nó không thực hiện hành vi này.

Tôi bối rối.

Đây là mã chạy thành công trên XP ...

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

Một câu hỏi Django tuyệt vời khác. Tôi đã thực hiện một số nỗ lực để giải quyết vấn đề này mà không có may mắn. Các tệp được tạo trong thư mục tải lên bị hỏng và chỉ có một phần kích thước so với bản gốc (được lưu trữ ở nơi khác).
westmark

CẬP NHẬT của bạn không hoạt động
AmiNadimi

Câu trả lời:


166

Tôi có một số mã tìm nạp hình ảnh từ trang web và lưu trữ nó trong một mô hình. Các bit quan trọng là:

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

Điều đó hơi khó hiểu vì nó đã rút ra khỏi mô hình của tôi và một chút ra khỏi bối cảnh, nhưng các phần quan trọng là:

  • Hình ảnh được kéo từ web không được lưu trong thư mục upload_to, thay vào đó, nó được lưu dưới dạng tempfile bởi urllib.urlretrieve () và sau đó bị loại bỏ.
  • Phương thức ImageField.save () lấy một tên tệp (bit os.path.basename) và một đối tượng django.core.files.File.

Hãy cho tôi biết nếu bạn có câu hỏi hoặc cần làm rõ.

Chỉnh sửa: để rõ ràng, đây là mô hình (trừ mọi báo cáo nhập khẩu bắt buộc):

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ôi đã thử một cái gì đó cho hiệu ứng này, nhưng có lẽ tôi sẽ thử lại lần nữa, trên thực tế, tôi có mã trông rất giống với mã này. (Ngay cả khi nó nằm ngoài ngữ cảnh tôi có thể thấy nó hoạt động như thế nào).
T. Stone

2
Tôi cũng khuyên bạn nên sử dụng phân tích cú pháp url để tránh bị dính url paramatar gunk vào hình ảnh. import urlparse. os.path.basename(urlparse.urlparse(self.url).path). Cảm ơn cho bài viết, đã hữu ích.
dennmat

1
Tôi nhận được django.core.exceptions.SuspicyOperation: Đã cố truy cập vào '/images/10.jpg'.
DataGreed

2
@DataGreed bạn nên xóa dấu gạch chéo '/' khỏi định nghĩa upload_to trong mô hình. Điều này đã được giải quyết ở đây .
tsikov

Tôi đang gặp lỗi như thế này:prohibited to prevent data loss due to unsaved related object 'stream'.
Dipak

95

Siêu dễ nếu mô hình chưa được tạo:

Đầu tiên , sao chép tệp hình ảnh của bạn vào đường dẫn tải lên (giả sử = 'path /' trong đoạn mã sau).

Thứ hai , sử dụng một cái gì đó như:

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

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

đã thử nghiệm và làm việc trong django 1.4, nó cũng có thể hoạt động với một mô hình hiện có.


10
Đây là câu trả lời đúng, cần nhiều phiếu hơn !!! Tìm thấy giải pháp này ở đây là tốt.
Andrew Swihart

Chào. Tôi có một câu hỏi. Tôi đang sử dụng kho lưu trữ django với phụ trợ Amazon S3. Điều này sẽ kích hoạt một tải lên mới?
Salvatore Iovene

OP yêu cầu "không cần Django cố gắng lưu trữ lại tệp" và đây là câu trả lời cho điều đó!
frnhr

2
Django có một số logic hiện có để giải thích cho tên tệp trùng lặp trên đĩa. Phương pháp này ghi lại logic đó vì người dùng còn lại để kiểm tra trùng lặp tên tệp.
Chris Conlan

1
@Conlan: nối một hướng dẫn vào tên tệp.
Rabih Kodeih

41

Chỉ cần một nhận xét nhỏ. câu trả lời tvon hoạt động nhưng, nếu bạn đang làm việc trên windows, có lẽ bạn muốn open()tập tin với 'rb'. Như thế này:

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

hoặc bạn sẽ nhận được tệp của bạn bị cắt ở 0x1Abyte đầu tiên .


1
Cảm ơn, tôi có xu hướng quên các cửa sổ chi tiết cấp thấp như vậy đối mặt với chúng tôi.
mike_k

fml ... điều gì xảy ra khi tham số đó được truyền vào trên máy linux?
DMac Kẻ hủy diệt

1
đã trả lời câu hỏi của riêng tôi ... xin lỗi vì thư rác. tìm thấy một số tài liệu cho việc này ở đây . "Trên Unix, việc thêm 'b' vào chế độ sẽ không ảnh hưởng gì, vì vậy bạn có thể sử dụng nó độc lập với nền tảng cho tất cả các tệp nhị phân."
DMac Kẻ hủy diệt

16

Đây là một phương pháp hoạt động tốt và cũng cho phép bạn chuyển đổi tệp sang một định dạng nhất định (để tránh lỗi "không thể ghi chế độ P dưới dạng 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)

trong đó hình ảnh là django ImageField hoặc your_model_instance.image ở đây là một ví dụ sử dụng:

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

Hi vọng điêu nay co ich


12

Ok, nếu tất cả những gì bạn cần làm là liên kết đường dẫn tệp hình ảnh đã có với ImageField, thì giải pháp này có thể hữu ích:

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

Chà, nếu nghiêm túc, tệp hình ảnh đã có sẽ không được liên kết với ImageField, nhưng bản sao của tệp này sẽ được tạo trong upload_to dir dưới dạng 'imgfilename.jpg' và sẽ được liên kết với ImageField.


2
Bạn có mở nó dưới dạng tệp nhị phân không?
Mariusz Jamro 8/2/2016

Giống như @MariuszJamro đã nói, nó phải như thế này:with open('/path/to/already/existing/file', 'rb') as f:
rahmatns

cũng đừng quên lưu đối tượng:obj.save()
rahmatns

11

Những gì tôi đã làm là tạo bộ nhớ của riêng tôi mà sẽ không lưu tệp vào đĩa:

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

Sau đó, trong các mô hình của tôi, cho ImageField của tôi, tôi đã sử dụng bộ lưu trữ tùy chỉnh mới:

from custom_storage import CustomStorage

custom_store = CustomStorage()

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

7

Nếu bạn chỉ muốn "đặt" tên tệp thực tế mà không phải chịu chi phí tải và lưu lại tệp (!!) hoặc sử dụng charfield (!!!), bạn có thể muốn thử một cái gì đó như thế này - -

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

Điều này sẽ làm sáng model_instance.myfile.url của bạn và tất cả những thứ còn lại giống như bạn thực sự đã tải tệp lên.

Giống như @ t-Stone nói, điều chúng tôi thực sự muốn, là có thể đặt instance.myfile.path = 'my-filename.jpg', nhưng Django hiện không hỗ trợ điều đó.


Nếu model_instance là phiên bản của mô hình chứa tệp .. thì "thể hiện" kia có nghĩa là gì ??
h3.

7

Theo tôi, giải pháp đơn giản nhất:

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

Rất nhiều câu trả lời đã lỗi thời và tôi đã dành nhiều giờ trong sự thất vọng (nói chung tôi khá mới đối với Django và nhà phát triển web nói chung). Tuy nhiên, tôi đã tìm thấy ý chính tuyệt vời này của @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

Đây có thể không phải là câu trả lời bạn đang tìm kiếm. nhưng bạn có thể sử dụng charfield để lưu trữ đường dẫn của tệp thay vì ImageFile. Theo cách đó, bạn có thể lập trình liên kết hình ảnh được tải lên với trường mà không cần tạo lại tệp.


Vâng, tôi đã cố gắng từ bỏ việc này và viết thư trực tiếp cho MySQL hoặc chỉ sử dụng CharField ().
T. Stone

1

Bạn co thể thử:

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

Đang làm việc! Bạn có thể lưu hình ảnh bằng cách sử dụng FileSystemStorage. kiểm tra ví dụ dưới đây

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)

Cảm ơn bạn Nids, hoàn toàn làm việc giải pháp này! Bạn đã tiết kiệm rất nhiều thời gian của tôi :)
Mehmet Burak Ibis

0

Bạn có thể sử dụng khung Django REST và thư viện Yêu cầu python để lưu hình ảnh theo chương trình vào Django ImageField

Đây là một ví dụ:

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

Với Django 3, với một mô hình như thế này:

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

nếu hình ảnh đã được tải lên, chúng ta có thể trực tiếp làm:

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

hoặc là

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.