Comment puis-je restreindre FileField
à accepter uniquement un certain type de fichier (vidéo, audio, pdf, etc.) de manière élégante, côté serveur?
Un moyen très simple consiste à utiliser un validateur personnalisé.
Dans le validators.py
De votre application:
def validate_file_extension(value):
import os
from Django.core.exceptions import ValidationError
ext = os.path.splitext(value.name)[1] # [0] returns path+filename
valid_extensions = ['.pdf', '.doc', '.docx', '.jpg', '.png', '.xlsx', '.xls']
if not ext.lower() in valid_extensions:
raise ValidationError(u'Unsupported file extension.')
Puis dans votre models.py
:
from .validators import validate_file_extension
... et utilisez le validateur pour votre champ de formulaire:
class Document(models.Model):
file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension])
Voir aussi: Comment limiter les types de fichiers lors du téléchargement de fichiers pour ModelForms avec FileFields? .
Django dans la version 1.11
a un FileExtensionValidator
nouvellement ajouté pour les champs du modèle, les documents sont ici: https://docs.djangoproject.com/en/dev/ref/validators/#fileextensionvalidator .
Un exemple de validation d'une extension de fichier:
from Django.core.validators import FileExtensionValidator
from Django.db import models
class MyModel(models.Model):
pdf_file = models.FileField(upload_to='foo/',
validators=[FileExtensionValidator(allowed_extensions=['pdf'])])
Notez que cette méthode n'est pas non sûre . Citation de Django docs:
Ne vous fiez pas à la validation de l'extension de fichier pour déterminer le type d'un fichier. Les fichiers peuvent être renommés pour avoir n'importe quelle extension, quelles que soient les données qu'ils contiennent.
Il y a aussi de nouveaux validate_image_file_extension
( https://docs.djangoproject.com/en/dev/ref/validators/#validate-image-file-extension ) pour valider les extensions d'image (à l'aide de Pillow).
Il y a un extrait de Django qui fait ceci:
import os
from Django import forms
class ExtFileField(forms.FileField):
"""
Same as forms.FileField, but you can specify a file extension whitelist.
>>> from Django.core.files.uploadedfile import SimpleUploadedFile
>>>
>>> t = ExtFileField(ext_whitelist=(".pdf", ".txt"))
>>>
>>> t.clean(SimpleUploadedFile('filename.pdf', 'Some File Content'))
>>> t.clean(SimpleUploadedFile('filename.txt', 'Some File Content'))
>>>
>>> t.clean(SimpleUploadedFile('filename.exe', 'Some File Content'))
Traceback (most recent call last):
...
ValidationError: [u'Not allowed filetype!']
"""
def __init__(self, *args, **kwargs):
ext_whitelist = kwargs.pop("ext_whitelist")
self.ext_whitelist = [i.lower() for i in ext_whitelist]
super(ExtFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(ExtFileField, self).clean(*args, **kwargs)
filename = data.name
ext = os.path.splitext(filename)[1]
ext = ext.lower()
if ext not in self.ext_whitelist:
raise forms.ValidationError("Not allowed filetype!")
#-------------------------------------------------------------------------
if __== "__main__":
import doctest, datetime
doctest.testmod()
Premier. Créez un fichier nommé formatChecker.py dans l'application où vous avez le modèle qui a le FileField que vous souhaitez accepter un certain type de fichier.
Voici votre formatChecker.py:
from Django.db.models import FileField
from Django.forms import forms
from Django.template.defaultfilters import filesizeformat
from Django.utils.translation import ugettext_lazy as _
class ContentTypeRestrictedFileField(FileField):
"""
Same as FileField, but you can specify:
* content_types - list containing allowed content_types. Example: ['application/pdf', 'image/jpeg']
* max_upload_size - a number indicating the maximum file size allowed for upload.
2.5MB - 2621440
5MB - 5242880
10MB - 10485760
20MB - 20971520
50MB - 5242880
100MB 104857600
250MB - 214958080
500MB - 429916160
"""
def __init__(self, *args, **kwargs):
self.content_types = kwargs.pop("content_types")
self.max_upload_size = kwargs.pop("max_upload_size")
super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
def clean(self, *args, **kwargs):
data = super(ContentTypeRestrictedFileField, self).clean(*args, **kwargs)
file = data.file
try:
content_type = file.content_type
if content_type in self.content_types:
if file._size > self.max_upload_size:
raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(self.max_upload_size), filesizeformat(file._size)))
else:
raise forms.ValidationError(_('Filetype not supported.'))
except AttributeError:
pass
return data
Seconde. Dans votre models.py, ajoutez ceci:
from formatChecker import ContentTypeRestrictedFileField
Ensuite, au lieu d'utiliser "FileField", utilisez ce "ContentTypeRestrictedFileField".
Exemple:
class Stuff(models.Model):
title = models.CharField(max_length=245)
handout = ContentTypeRestrictedFileField(upload_to='uploads/', content_types=['video/x-msvideo', 'application/pdf', 'video/mp4', 'audio/mpeg', ],max_upload_size=5242880,blank=True, null=True)
Ce sont les choses que vous devez faire lorsque vous souhaitez accepter uniquement un certain type de fichier dans FileField.
Vous pouvez utiliser ce qui suit pour restreindre les types de fichiers dans votre formulaire
file = forms.FileField(widget=forms.FileInput(attrs={'accept':'application/pdf'}))
Quelques personnes ont suggéré d'utiliser python-magic pour valider que le fichier est réellement du type que vous attendez. Cela peut être incorporé dans le validator
suggéré dans la réponse acceptée:
import os
import magic
from Django.core.exceptions import ValidationError
def validate_is_pdf(file):
valid_mime_types = ['application/pdf']
file_mime_type = magic.from_buffer(file.read(1024), mime=True)
if file_mime_type not in valid_mime_types:
raise ValidationError('Unsupported file type.')
valid_file_extensions = ['.pdf']
ext = os.path.splitext(file.name)[1]
if ext.lower() not in valid_file_extensions:
raise ValidationError('Unacceptable file extension.')
Cet exemple ne valide qu'un pdf, mais un nombre illimité de types MIME et d'extensions de fichiers peuvent être ajoutés aux tableaux.
En supposant que vous avez enregistré ce qui précède dans validators.py
vous pouvez incorporer ceci dans votre modèle comme ceci:
from myapp.validators import validate_is_pdf
class PdfFile(models.Model):
file = models.FileField(upload_to='pdfs/', validators=(validate_is_pdf,))
Je pense que vous seriez mieux adapté en utilisant le ExtFileField que Dominic Rodger a spécifié dans sa réponse et python-magic que Daniel Quinn a mentionné est la meilleure façon de procéder. Si quelqu'un est assez intelligent pour changer l'extension, au moins, vous l'attraperez avec les en-têtes.
Vous pouvez définir une liste de types MIME acceptés dans les paramètres, puis définir un validateur qui utilise python-magic pour détecter le type MIME et déclenche ValidationError si le type MIME n'est pas accepté. Définissez ce validateur dans le champ du formulaire de fichier.
Le seul problème est que parfois le type MIME est application/octet-stream, ce qui peut correspondre à différents formats de fichiers. Est-ce que quelqu'un d'entre vous a surmonté ce problème?
De plus, je vais étendre cette classe avec un comportement supplémentaire.
class ContentTypeRestrictedFileField(forms.FileField):
...
widget = None
...
def __init__(self, *args, **kwargs):
...
self.widget = forms.ClearableFileInput(attrs={'accept':kwargs.pop('accept', None)})
super(ContentTypeRestrictedFileField, self).__init__(*args, **kwargs)
Lorsque nous créons une instance avec param accept = ". Pdf, .txt", dans une fenêtre contextuelle avec une structure de fichier par défaut, nous verrons les fichiers avec l'extension passée.