42

Próbuję przekonwertować obiekt na obiekt PIL Image, aby go miniaturować, a następnie przekonwertować obiekt PIL Image, który moja funkcja miniatur wraca z powrotem do obiektu File. Jak mogę to zrobić?Jak przekonwertować plik PIL `Image` na plik Django` File`?

+0

Zdjęcie do pliku Django? –

+2

@anand PIL 'Obraz' instancji do instancji Django' File'. "Plik" Django jest podklasą klasy "Plik" Pythona. – orokusaki

+1

python 3 rozwiązanie tutaj http://stackoverflow.com/a/30435175/3033586 – madzohan

Odpowiedz

89

Sposobem na to zrobić bez konieczności pisania z powrotem do systemu plików, a następnie przenieść plik z powrotem do pamięci za pośrednictwem otwartego zaproszenia , jest użycie StringIO i Django InMemoryUploadedFile. Oto krótka próbka, w jaki sposób możesz to zrobić. Zakłada się, że masz już miniaturowy obraz o nazwie "kciuk":

import StringIO 

from django.core.files.uploadedfile import InMemoryUploadedFile 

# Create a file-like object to write thumb data (thumb data previously created 
# using PIL, and stored in variable 'thumb') 
thumb_io = StringIO.StringIO() 
thumb.save(thumb_io, format='JPEG') 

# Create a new Django file-like object to be used in models as ImageField using 
# InMemoryUploadedFile. If you look at the source in Django, a 
# SimpleUploadedFile is essentially instantiated similarly to what is shown here 
thumb_file = InMemoryUploadedFile(thumb_io, None, 'foo.jpg', 'image/jpeg', 
            thumb_io.len, None) 

# Once you have a Django file-like object, you may assign it to your ImageField 
# and save. 
... 

Daj mi znać, jeśli potrzebujesz więcej wyjaśnień. Mam to teraz działa w moim projekcie, ładowanie do S3 przy użyciu Django-Storages. Zajęło mi to większą część dnia, aby właściwie znaleźć rozwiązanie tutaj.

+0

Ładne i proste. Działa bezbłędnie w sygnale pre_save. – jmagnusson

+22

Odkryłem, że możesz zmniejszyć ilość pracy nawet odrobinę więcej, używając zamiast tego klasy Django ContentFile. W takim przypadku zaimportuj plik ContentFile z pliku django.core.files.base, a następnie wykonaj: thumb_file = ContentFile (thumb_io.getvalue()) – Bialecki

+0

Dziękuję Bialecki za tę odpowiedź. Program ContentFile działał doskonale, mimo że nie mogłem go uruchomić przy użyciu InMemoryUploadedFile. – Spike

11

Musiałem to zrobić w kilku krokach, imagejpeg() w php wymaga podobnego procesu. Nie znaczy to, że nie ma sposobu, aby zachować rzeczy w pamięci, ale ta metoda daje odniesienie do pliku zarówno oryginalnego obrazu i kciuka (zwykle dobry pomysł, na wypadek, gdyby trzeba było wrócić i zmienić swój rozmiar kciuka).

  1. zapisać plik
  2. otworzyć go z systemu plików z PIL,
  3. zapisać do katalogu temp z PIL,
  4. następnie otwarty Jako plik Django to zadziałało.

Model:

class YourModel(Model): 
    img = models.ImageField(upload_to='photos') 
    thumb = models.ImageField(upload_to='thumbs') 

Zastosowanie:

#in upload code 
uploaded = request.FILES['photo'] 
from django.core.files.base import ContentFile 
file_content = ContentFile(uploaded.read()) 
new_file = YourModel() 
#1 - get it into the DB and file system so we know the real path 
new_file.img.save(str(new_file.id) + '.jpg', file_content) 
new_file.save() 

from PIL import Image 
import os.path 

#2, open it from the location django stuck it 
thumb = Image.open(new_file.img.path) 
thumb.thumbnail(100, 100) 

#make tmp filename based on id of the model 
filename = str(new_file.id) 

#3. save the thumbnail to a temp dir 

temp_image = open(os.path.join('/tmp',filename), 'w') 
thumb.save(temp_image, 'JPEG') 

#4. read the temp file back into a File 
from django.core.files import File 
thumb_data = open(os.path.join('/tmp',filename), 'r') 
thumb_file = File(thumb_data) 

new_file.thumb.save(str(new_file.id) + '.jpg', thumb_file) 
+7

bardzo niezoptymalizowany, robisz operacje IO w kroku 1,2,3,4. –

1

Oto aplikacja, która może to zrobić: django-smartfields

from django.db import models 

from smartfields import fields 
from smartfields.dependencies import FileDependency 
from smartfields.processors import ImageProcessor 

class ImageModel(models.Model): 
    image = fields.ImageField(dependencies=[ 
     FileDependency(processor=ImageProcessor(
      scale={'max_width': 150, 'max_height': 150})) 
    ]) 

Upewnij się, aby przejść keep_orphans=True na pole, jeśli chcesz zachować stare pliki, w przeciwnym razie są one czyszczone po wymianie.

+0

Dobrze, dziękuję, to jest naprawdę świetne. – orokusaki

0

Dla osób korzystających django-storages/-redux zapisać plik obrazu na S3, oto ścieżka wziąłem (Poniższy przykład tworzy miniatury istniejącego obrazu):

from PIL import Image 
import StringIO 
from django.core.files.storage import default_storage 

try: 
    # example 1: use a local file 
    image = Image.open('my_image.jpg') 
    # example 2: use a model's ImageField 
    image = Image.open(my_model_instance.image_field) 
    image.thumbnail((300, 200)) 
except IOError: 
    pass # handle exception 

thumb_buffer = StringIO.StringIO() 
image.save(thumb_buffer, format=image.format) 
s3_thumb = default_storage.open('my_new_300x200_image.jpg', 'w') 
s3_thumb.write(thumb_buffer.getvalue()) 
s3_thumb.close() 
3

To jest rzeczywisty przykład pracuje python 3.5 i Django 1.10

w views.py:

from io import BytesIO 
from django.core.files.base import ContentFile 
from django.core.files.uploadedfile import InMemoryUploadedFile 

def pill(image_io): 
    im = Image.open(image_io) 
    ltrb_border = (0, 0, 0, 10) 
    im_with_border = ImageOps.expand(im, border=ltrb_border, fill='white') 

    buffer = BytesIO() 
    im_with_border.save(fp=buffer, format='JPEG') 
    buff_val = buffer.getvalue() 
    return ContentFile(buff_val) 

def save_img(request) 
    if request.POST: 
     new_record = AddNewRecordForm(request.POST, request.FILES) 
     pillow_image = pill(request.FILES['image']) 
     image_file = InMemoryUploadedFile(pillow_image, None, 'foo.jpg', 'image/jpeg', pillow_image.tell, None) 
     request.FILES['image'] = image_file # really need rewrite img in POST for success form validation 
     new_record.image = request.FILES['image'] 
     new_record.save() 
     return redirect(...) 
1

Łączenie komentarzy i aktualizacji dla Python 3+

Powiązane problemy