42

Je suis en train de convertir un UploadedFile en un objet PIL Image pour le vignette, puis convertir l'objet PIL Image que ma fonction de miniatures retourne en un objet File. Comment puis-je faire ceci?Comment convertir un PIL `Image` en un` File` Django?

+0

Image au fichier Django? –

+2

@anand Une instance PIL 'Image' vers une instance Django' File'. Le 'File' de Django est une sous-classe de la classe' File' de Python. – orokusaki

+1

solution python 3 ici http://stackoverflow.com/a/30435175/3033586 – madzohan

Répondre

89

La façon de le faire sans avoir à écrire de nouveau au système de fichiers, puis mettre le fichier dans la mémoire via un appel , est de faire usage de StringIO et Django InMemoryUploadedFile. Voici un exemple rapide sur la façon dont vous pourriez faire cela. Cela suppose que vous avez déjà une image miniature nommée 'thumb':

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

Faites-moi savoir si vous avez besoin de plus de précisions. J'ai ce travail dans mon projet en ce moment, en téléchargeant sur S3 en utilisant django-stockages. Cela m'a pris la meilleure partie d'une journée pour trouver correctement la solution ici.

+0

Nice et simple. Travailler parfaitement dans le signal pre_save. – jmagnusson

+22

J'ai trouvé que vous pouviez réduire la quantité de travail encore plus en utilisant la classe Django ContentFile à la place. Dans ce cas, importer ContentFile à partir de django.core.files.base et puis vous feriez: thumb_file = ContentFile (thumb_io.getvalue()) – Bialecki

+0

Merci Bialecki pour cette réponse. ContentFile a très bien fonctionné pour moi même si je n'ai pas réussi à l'utiliser avec InMemoryUploadedFile. – Spike

11

J'ai dû faire cela en quelques étapes, imagejpeg() en php nécessite un processus similaire. Pour ne pas dire qu'il n'y a aucun moyen de garder les choses en mémoire, mais cette méthode vous donne une référence de fichier à la fois l'image originale et le pouce (habituellement une bonne idée au cas où vous devriez revenir en arrière et changer la taille de votre pouce).

  1. Enregistrez le fichier
  2. ouvrir du système de fichiers avec PIL,
  3. Enregistrer dans un répertoire temporaire avec PIL,
  4. puis ouvrez le fichier en tant que Django pour que cela fonctionne.

Modèle:

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

Utilisation:

#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

hautement non optimisé, vous effectuez des opérations d'E/S à l'étape 1,2,3,4. –

1

Voici une application qui peut le faire: 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})) 
    ]) 

Assurez-vous de passer keep_orphans=True sur le terrain, si vous voulez conserver les anciens fichiers, sinon ils sont nettoyés lors de leur remplacement.

+0

Excellent, merci, c'est vraiment génial. – orokusaki

0

Pour ceux qui utilisent django-storages/-redux pour stocker le fichier image sur S3, voici le chemin que je pris (l'exemple ci-dessous crée une vignette d'une image existante):

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

Ceci est par exemple de travail réel pour python 3.5 et django 1.10

dans 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

Mettre ensemble des commentaires et des mises à jour pour Python 3+

from io import BytesIO 
import requests 

    # Read a file in 

    r = request.get(image_url) 
    image = r.content 
    scr = Image.open(BytesIO(image)) 

    # Perform an image operation like resize: 

    width, height = scr.size 
    new_width = 320 
    new_height = int(new_width * height/width) 
    img = scr.resize((new_width, new_height)) 

    # Get the Django file object 

    thumb_io = io.BytesIO() 
    img.save(thumb_io, format='JPEG') 
    photo_smaller = ContentFile(thumb_io.getvalue())