Wie kann ich einschränken FileField
, dass nur ein bestimmter Dateityp (Video, Audio, PDF usw.) auf elegante Weise serverseitig akzeptiert wird?
Wie kann ich einschränken FileField
, dass nur ein bestimmter Dateityp (Video, Audio, PDF usw.) auf elegante Weise serverseitig akzeptiert wird?
Antworten:
Eine sehr einfache Möglichkeit ist die Verwendung eines benutzerdefinierten Validators.
In Ihrer App validators.py
:
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('Unsupported file extension.')
Dann in Ihrem models.py
:
from .validators import validate_file_extension
... und verwenden Sie den Validator für Ihr Formularfeld:
class Document(models.Model):
file = models.FileField(upload_to="documents/%Y/%m/%d", validators=[validate_file_extension])
Siehe auch: Wie kann ich Dateitypen beim Hochladen von Dateien für ModelForms mit FileFields einschränken? .
Warnung
Zum Sichern Ihrer Codeausführungsumgebung vor schädlichen Mediendateien
- Verwenden Sie Exif- Bibliotheken , um die Mediendateien ordnungsgemäß zu überprüfen.
- Trennen Sie Ihre Mediendateien von Ihrer Anwendungscode-Ausführungsumgebung
- Verwenden Sie nach Möglichkeit Lösungen wie S3, GCS, Minio oder ähnliches
- Verwenden Sie beim Laden von Mediendateien auf der Clientseite native Client-Methoden (wenn Sie beispielsweise die Mediendateien nicht sicher in einem Browser laden, kann dies zur Ausführung von "gestaltetem" JavaScript-Code führen).
Django in der Version 1.11
hat einen neu hinzugefügten FileExtensionValidator
für Modellfelder. Die Dokumentation finden Sie hier: https://docs.djangoproject.com/en/dev/ref/validators/#fileextensionvalidator .
Ein Beispiel für die Überprüfung einer Dateierweiterung:
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'])])
Beachten Sie, dass diese Methode nicht sicher ist . Zitat aus Django-Dokumenten:
Verlassen Sie sich nicht auf die Überprüfung der Dateierweiterung, um den Dateityp zu bestimmen. Dateien können umbenannt werden, um eine beliebige Erweiterung zu haben, unabhängig davon, welche Daten sie enthalten.
Es gibt auch neue validate_image_file_extension
( https://docs.djangoproject.com/de/dev/ref/validators/#validate-image-file-extension ) zum Überprüfen von Bilderweiterungen (mit Pillow).
allowed_extensions
aus irgendeinem Grund nicht hinzugefügt wurde . Verwenden des Django Rest Framework.
Es gibt einen Django-Ausschnitt , der dies tut:
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 __name__ == "__main__":
import doctest, datetime
doctest.testmod()
Zuerst. Erstellen Sie eine Datei mit dem Namen formatChecker.py in der App, in der Sie das Modell mit dem FileField haben, das Sie für einen bestimmten Dateityp akzeptieren möchten.
Dies ist Ihr 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
Zweite. Fügen Sie in Ihrer models.py Folgendes hinzu:
from formatChecker import ContentTypeRestrictedFileField
Verwenden Sie dann anstelle von 'FileField' dieses 'ContentTypeRestrictedFileField'.
Beispiel:
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)
Dies sind die Dinge, die Sie tun müssen, wenn Sie nur einen bestimmten Dateityp in FileField akzeptieren möchten.
Einige Leute haben vorgeschlagen, Python-Magic zu verwenden, um zu überprüfen, ob die Datei tatsächlich von dem Typ ist, den Sie erwarten. Dies kann in die validator
in der akzeptierten Antwort vorgeschlagene Antwort aufgenommen werden:
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.')
In diesem Beispiel wird nur ein PDF überprüft, es können jedoch beliebig viele MIME-Typen und Dateierweiterungen zu den Arrays hinzugefügt werden.
Angenommen, Sie haben das oben Gesagte gespeichert validators.py
, können Sie dies wie folgt in Ihr Modell integrieren:
from myapp.validators import validate_is_pdf
class PdfFile(models.Model):
file = models.FileField(upload_to='pdfs/', validators=(validate_is_pdf,))
Ich denke, Sie wären am besten geeignet, wenn Sie das ExtFileField verwenden würden , das Dominic Rodger in seiner Antwort angegeben hat, und die von Daniel Quinn erwähnte Python-Magie ist der beste Weg. Wenn jemand klug genug ist, um zumindest die Erweiterung zu ändern, werden Sie ihn mit den Headern abfangen.
Sie können eine Liste der akzeptierten MIME-Typen in den Einstellungen definieren und dann einen Validator definieren, der Python-Magic verwendet, um den MIME-Typ zu erkennen, und ValidationError auslöst, wenn der MIME-Typ nicht akzeptiert wird. Setzen Sie diesen Validator in das Dateiformatfeld.
Das einzige Problem ist, dass der MIME-Typ manchmal Application / Octet-Stream ist, was verschiedenen Dateiformaten entsprechen kann. Hat jemand von Ihnen dieses Problem gelöst?
Zusätzlich werde ich diese Klasse mit etwas zusätzlichem Verhalten erweitern.
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)
Wenn wir eine Instanz mit param accept = ".pdf, .txt" erstellen, werden im Popup mit der Dateistruktur als Standard standardmäßig Dateien mit übergebener Erweiterung angezeigt.
Nachdem ich die akzeptierte Antwort überprüft hatte, beschloss ich, einen Tipp basierend auf der Django-Dokumentation zu teilen. Es gibt bereits einen Validator zur Validierung der Dateierweiterung. Sie müssen Ihre eigene benutzerdefinierte Funktion nicht neu schreiben, um zu überprüfen, ob Ihre Dateierweiterung zulässig ist oder nicht.
https://docs.djangoproject.com/de/3.0/ref/validators/#fileextensionvalidator
Warnung
Verlassen Sie sich nicht auf die Überprüfung der Dateierweiterung, um den Dateityp zu bestimmen. Dateien können umbenannt werden, um eine beliebige Erweiterung zu haben, unabhängig davon, welche Daten sie enthalten.