Görüntüyü programlı olarak Django ImageField klasörüne kaydetme


203

Tamam, her şeyin yakınında denedim ve işe yarayamıyorum.

  • Bir ImageField ile bir Django modeli var
  • HTTP aracılığıyla bir resim indiren kodum var (test edildi ve çalışıyor)
  • Görüntü doğrudan 'upload_to' klasörüne kaydedilir (upload_to, ImageField üzerinde ayarlanan dosyadır)
  • Tek yapmam gereken mevcut görüntü dosyası yolunu ImageField ile ilişkilendirmek.

Bu kodu yaklaşık 6 farklı şekilde yazdım.

Karşılaştığım sorun, yazdığım kodun tümünün aşağıdaki davranışla sonuçlanmasıdır: (1) Django 2. bir dosya oluşturur, (2) dosyanın sonuna _ ekleyerek yeni dosyayı yeniden adlandırır sonra (3) hiçbir veriyi temelde yeniden adlandırılmış boş bir dosya bırakarak aktarmaz. 'Upload_to' yolunda kalan, gerçek resim olan ve resmin adı olan, ancak boş olan 2 dosyadır ve tabii ki ImageField yolu Django'nun oluşturmaya çalıştığı boş dosyaya ayarlanmıştır .

Bu net değilse, açıklamaya çalışacağım:

## 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'nun dosyayı yeniden saklamaya çalışmadan bunu nasıl yapabilirim? Gerçekten istediğim şey bu etki için bir şey ...

model.ImageField.path = generated_image_path

... ama elbette bu işe yaramıyor.

Ve evet ben gibi burada diğer sorular geçtiniz bu bir ilgili olanların yanı sıra Django doc Dosya

GÜNCELLEME Daha fazla sınamadan sonra, bu davranış yalnızca Windows Server'da Apache altında çalışırken yapılır. XP'de 'runserver' altında çalışırken bu davranışı yürütmez.

Çok üzüldüm.

İşte XP'de başarıyla çalışan kod ...

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

Bir başka harika Django sorusu. Bu sorunu şanssız çözmek için birkaç girişimde bulundum. Yükleme dizininde oluşturulan dosyalar kırılır ve orijinallere kıyasla (yalnızca başka bir yerde saklanır) boyut olarak bir kesir.
westmark

Yanıtlar:


168

Ben bir görüntü web kapalı alır ve bir modelde saklar bazı kod var. Önemli bitler:

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

Bu biraz kafa karıştırıcı, çünkü modelimden ve biraz bağlamdan çıkarıldı, ancak önemli kısımlar:

  • Ağ-çekilmiş görüntüsü olup olmayan upload_to klasörde saklanan, bunun yerine urllib.urlretrieve () ve daha sonra atıldı bir TempFile olarak depolanır.
  • ImageField.save () yöntemi bir dosya adı (os.path.basename biti) ve django.core.files.File nesnesini alır.

Sorularınız varsa veya açıklığa ihtiyacınız varsa bize bildirin.

Düzenleme: netlik için, burada model (gerekli ithalat beyanları eksi):

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 - Bu etki için bir şey denemiştim, ama belki başka bir şans vereceğim, aslında, buna çok benzeyen bir kodum vardı. (Bağlam dışında olsa bile nasıl çalıştığını görebiliyorum).
T. Stone

2
Görüntüye eklenmiş url paramatar gunk almamak için de url ayrıştırma özelliğini kullanmanızı öneririm. import urlparse. os.path.basename(urlparse.urlparse(self.url).path). Yazı için teşekkürler, yardımcı oldu.
dennmat

1
Django.core.exceptions.SuspiciousOperation: '/images/10.jpg' dosyasına erişim denendi reddedildi.
DataGreed

2
@DataGreed, '/' eğik çizgisini modeldeki upload_to tanımından kaldırmalısınız. Buraya değinildi .
tsikov

Böyle bir hata alıyorum:prohibited to prevent data loss due to unsaved related object 'stream'.
Dipak

96

Model henüz oluşturulmamışsa çok kolay:

İlk olarak , resim dosyanızı yükleme yoluna kopyalayın ( sonraki snippet'te = 'path /' olduğu varsayılır ).

İkincisi , şöyle bir şey kullanın:

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

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

django 1.4'te test edilmiş ve çalışıyorsa, mevcut bir model için de işe yarayabilir.


10
Bu doğru cevap, daha fazla oy gerekiyor !!! Bu çözümü burada da buldum.
Andrew Swihart

Selam. Bir sorum var. Amazon S3 arka ucuyla django depolarını kullanıyorum. Bu yeni bir yüklemeyi tetikleyecek mi?
Salvatore Iovene

OP, "Django'nun dosyayı yeniden saklamaya çalışmasına gerek kalmadan" soruyor ve bu da bunun cevabı!
frnhr

2
Django'nun diskteki yinelenen dosya adlarını açıklamak için var olan bazı mantığı vardır. Bu yöntem, kullanıcı dosya adı çoğaltmasını denetlemeye bırakıldığı için bu mantığı engeller.
Chris Conlan

1
@Conlan: dosya adına bir kılavuz ekleyin.
Rabih Kodeih

41

Sadece küçük bir açıklama. tvon yanıt çalışır, ancak pencerelerde çalışıyorsanız, muhtemelen open()dosya ile birlikte istersiniz 'rb'. Bunun gibi:

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

veya dosyanızı ilk 0x1Abaytta kısaltırsınız .


1
Teşekkürler, pencerelerin bize karşı karşıya kaldığı düşük seviyeli ayrıntıları unutma eğilimindeyim.
mike_k

fml ... linux makinesinde bu parametre iletildiğinde ne olur?
DMac Destroyer

1
kendi sorumu yanıtladı ... spam için üzgünüm. burada bunun için bazı belgeler buldum . "Unix'te, kipe" b "eklemek zarar vermez, böylece tüm ikili dosyalar için platformdan bağımsız olarak kullanabilirsiniz."
DMac Destroyer

16

İşte iyi çalışan ve dosyayı belirli bir formata dönüştürmenize izin veren bir yöntem ("P modu JPEG olarak yazılamıyor" hatasını önlemek için):

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)

burada görüntü django ImageField veya your_model_instance.image burada bir kullanım örneğidir:

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

Bu yardımcı olur umarım


12

Tamam, yapmanız gereken tek şey mevcut görüntü dosyası yolunu ImageField ile ilişkilendirmekse, bu çözüm yararlı olabilir:

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

Gerçekten, zaten mevcut olan görüntü dosyası ImageField ile ilişkilendirilmez, ancak bu dosyanın kopyası upload_to dizininde 'imgfilename.jpg' olarak oluşturulur ve ImageField ile ilişkilendirilir.


2
Bir ikili dosya olarak açmadınız mı?
Mariusz Jamro

@MariuszJamro'nun söylediği gibi, şöyle olmalı:with open('/path/to/already/existing/file', 'rb') as f:
rahmatns

Ayrıca nesneyi kaydetmeyi unutmayın:obj.save()
rahmatns

11

Yaptığım sadece dosyayı diske kaydetmeyecek kendi depolama alanı oluşturmaktı:

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

Ardından, modellerimde, ImageField'ım için yeni özel depolamayı kullandım:

from custom_storage import CustomStorage

custom_store = CustomStorage()

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

7

Gerçek dosya adını yalnızca "ayarlamak" istiyorsanız, dosyayı yükleme ve yeniden kaydetme yükünü (!!) yüklemeden veya bir charfield (!!!) kullanmaya başvurmadan, böyle bir şey denemek isteyebilirsiniz - -

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

Bu, model_instance.myfile.url dosyasını ve diğerlerini, dosyayı gerçekten yüklediğiniz gibi aydınlatacaktır.

@ T-stone'un dediği gibi, gerçekten istediğimiz şey, instance.myfile.path = 'dosyam.jpg' ayarlayabilmektir, ancak Django şu anda bunu desteklememektedir.


Model_instance dosyası içeren model örneğiyse .. diğer "örnek" ne anlama gelir?
h3.

7

Bence en basit çözüm:

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

Bu cevapların birçoğu modası geçmiş ve hayal kırıklığı içinde uzun saatler geçirdim (genel olarak Django & web dev için oldukça yeniyim). Ancak, @iambibhas tarafından bu mükemmel özeti buldum: 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

Aradığınız cevap bu olmayabilir. ancak charfield komutunu kullanarak dosyanın yolunu ImageFile yerine saklayabilirsiniz. Bu şekilde yüklenen görüntüyü, dosyayı yeniden oluşturmadan alanla programlı olarak ilişkilendirebilirsiniz.


Evet, bundan vazgeçmek ve doğrudan MySQL'e yazmak ya da sadece bir CharField () kullanmak için cazip davrandım.
T. Stone

1

Deneyebilirsin:

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

Çalışma! FileSystemStorage'ı kullanarak görüntü kaydedebilirsiniz. aşağıdaki örneği kontrol edin

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)

Teşekkürler Nids, kesinlikle bu çözümü çalışıyor! Çok zaman kazandın :)
Mehmet Burak Ibis

0

Görüntüyü Django ImageField'e programlı olarak kaydetmek için Django REST çerçevesini ve python İstekleri kitaplığını kullanabilirsiniz

İşte bir örnek:

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 ile, bunun gibi bir modelle:

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

resim zaten yüklenmişse, doğrudan şunları yapabiliriz:

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

veya

my_item = Item.objects.get(id=5)
my_item.photo='image_folder/sample_photo.png'
my_item.save()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.