Ich habe ein Formular in meiner Django-App, in das Benutzer Dateien hochladen können.
Wie kann ich ein Limit für die Größe der hochgeladenen Datei festlegen, damit das Formular nicht gültig ist und ein Fehler auftritt, wenn ein Benutzer eine Datei hochlädt, die größer als mein Limit ist?
django
file-upload
Daniels
quelle
quelle
Antworten:
Dieser Code könnte helfen:
# Add to your settings file CONTENT_TYPES = ['image', 'video'] # 2.5MB - 2621440 # 5MB - 5242880 # 10MB - 10485760 # 20MB - 20971520 # 50MB - 5242880 # 100MB 104857600 # 250MB - 214958080 # 500MB - 429916160 MAX_UPLOAD_SIZE = "5242880" #Add to a form containing a FileField and change the field names accordingly. from django.template.defaultfilters import filesizeformat from django.utils.translation import ugettext_lazy as _ from django.conf import settings def clean_content(self): content = self.cleaned_data['content'] content_type = content.content_type.split('/')[0] if content_type in settings.CONTENT_TYPES: if content._size > settings.MAX_UPLOAD_SIZE: raise forms.ValidationError(_('Please keep filesize under %s. Current filesize %s') % (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(content._size))) else: raise forms.ValidationError(_('File type is not supported')) return content
Entnommen aus: Django-Snippets - Validierung nach Art und Größe des Dateiinhalts
quelle
MiB
ist immer immer immer 1048576 Bytes, es ist keine Zehnerpotenz. WasMB
ist es nicht eindeutig, kann es entweder mittlere 1000000 Bytes , wenn Sie IEC - Normen sind folgende oder 1048576 Bytes , wenn Sie Windows und dergleichen verwenden. Der Wikipedia-Artikel, auf den Sie verlinkt haben, ist ein Beweis.content.size
(kein Unterstrich)MAX_UPLOAD_SIZE
Zeichenfolge. Es sollte eine Zahl sein - dieser Code ermöglicht das Hochladen jeder Größe, da der erste ValidationError nicht erreicht werden kann.Sie können dieses Snippet formatChecker verwenden. Was es tut ist
Hier können Sie angeben, welche Dateiformate hochgeladen werden dürfen.
und lässt Sie das Limit der Dateigröße der hochzuladenden Datei festlegen.
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", 0) 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)
Sie können den Wert von 'max_upload_size' auf das gewünschte Limit der Dateigröße ändern. Sie können auch die Werte in der Liste der 'content_types' in die Dateitypen ändern, die Sie akzeptieren möchten.
quelle
kwargs.pop
daran erinnern, dass sie einen Standard haben sollten. Ändern Sie zu diesemself.content_types = kwargs.pop("content_types", []) self.max_upload_size = kwargs.pop("max_upload_size", [])
Eine andere Lösung ist die Verwendung von Validatoren
from django.core.exceptions import ValidationError def file_size(value): # add this to some file where you can import it from limit = 2 * 1024 * 1024 if value.size > limit: raise ValidationError('File too large. Size should not exceed 2 MiB.')
dann haben Sie in Ihrem Formular mit dem Feld Datei so etwas
image = forms.FileField(required=False, validators=[file_size])
quelle
_size
und diese nicht.Ich glaube, dass das Django-Formular eine Datei erst empfängt, nachdem es vollständig hochgeladen wurde. Wenn jemand eine 2-GB-Datei hochlädt, ist es daher viel besser, wenn der Webserver die Größe im laufenden Betrieb überprüft.
Weitere Informationen finden Sie in diesem Mail-Thread .
quelle
Nur eine kurze Anmerkung zu dem Snippet, das in diesem Thread enthalten war:
Es war sehr nützlich, enthält jedoch einige kleinere Fehler. Robusterer Code sollte folgendermaßen aussehen:
# Add to your settings file CONTENT_TYPES = ['image', 'video'] # 2.5MB - 2621440 # 5MB - 5242880 # 10MB - 10485760 # 20MB - 20971520 # 50MB - 5242880 # 100MB - 104857600 # 250MB - 214958080 # 500MB - 429916160 MAX_UPLOAD_SIZE = "5242880" #Add to a form containing a FileField and change the field names accordingly. from django.template.defaultfilters import filesizeformat from django.utils.translation import ugettext_lazy as _ from django.conf import settings def clean_content(self): if content != None: content = self.cleaned_data['content'] content_type = content.content_type.split('/')[0] if content_type in settings.CONTENT_TYPES: if content._size > int(settings.MAX_UPLOAD_SIZE): raise forms.ValidationError(_(u'Please keep filesize under %s. Current filesize %s') % (filesizeformat(settings.MAX_UPLOAD_SIZE), filesizeformat(content._size))) else: raise forms.ValidationError(_(u'File type is not supported')) return content
Es gibt nur ein paar Verbesserungen:
Zuerst stelle ich fest, ob das Dateifeld leer ist (keine) - ohne es wird Django eine Ausnahme im Webbrowser auslösen.
Als nächstes geben Sie Casting in int (settings.MAX_UPLOAD_SIZE) ein, da dieser Einstellungswert eine Zeichenfolge ist. Zeichenfolgen können nicht zum Vergleichen mit Zahlen verwendet werden.
Last but not least das Unicode-Präfix 'u' in der ValidationError-Funktion.
Vielen Dank für diesen Ausschnitt!
quelle
'.pdf'
anstatt zucontent_type
prüfen. Dies ist einfacher und gleichzeitig robuster.Wenn jemand nach einer Formularvariante
FileField
der @angelo-Lösung sucht, dann ist sie hierfrom django import forms from django.template.defaultfilters import filesizeformat from django.utils.translation import ugettext_lazy as _ from django.core.exceptions import ValidationError class RestrictedFileField(forms.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(RestrictedFileField, self).__init__(*args, **kwargs) def clean(self, data, initial=None): file = super(RestrictedFileField, self).clean(data, initial) try: content_type = file.content_type if content_type in self.content_types: if file._size > self.max_upload_size: raise ValidationError(_('Please keep filesize under %s. Current filesize %s') % ( filesizeformat(self.max_upload_size), filesizeformat(file._size))) else: raise ValidationError(_('Filetype not supported.')) except AttributeError: pass return data
Dann erstellen Sie ein Formular als
class ImageUploadForm(forms.Form): """Image upload form.""" db_image = RestrictedFileField(content_types=['image/png', 'image/jpeg'], max_upload_size=5242880)
quelle
Serverseite
Meine bevorzugte Methode, um zu überprüfen, ob eine Datei serverseitig zu groß ist, ist die Antwort von ifedapo olarewaju mithilfe eines Validators.
Client-Seite
Das Problem, nur eine serverseitige Validierung durchzuführen, besteht darin, dass die Validierung erst nach Abschluss des Uploads erfolgt. Stellen Sie sich vor, Sie laden eine riesige Datei hoch und warten ewig, um anschließend zu erfahren, dass die Datei zu groß ist. Wäre es nicht schöner, wenn der Browser mich vorher darüber informieren könnte, dass die Datei zu groß ist?
Nun, es gibt einen Weg zu dieser Client-Seite , indem Sie die HTML5-Datei-API verwenden !
Hier ist das erforderliche Javascript (abhängig von JQuery):
$("form").submit(function() { if (window.File && window.FileReader && window.FileList && window.Blob) { var file = $('#id_file')[0].files[0]; if (file && file.size > 2 * 1024 * 1024) { alert("File " + file.name + " of type " + file.type + " is too big"); return false; } } });
Natürlich benötigen Sie noch eine serverseitige Überprüfung, um sich vor böswilligen Eingaben zu schützen, und Benutzer, für die Javascript nicht aktiviert ist.
quelle
Eine andere elegante Lösung mit Validatoren, die die maximale Dateigröße nicht fest codiert, ist die Verwendung eines klassenbasierten Validators:
from django.core.exceptions import ValidationError from django.core.validators import MaxValueValidator from django.utils.translation import ugettext as _ class MaxSizeValidator(MaxValueValidator): message = _('The file exceed the maximum size of %(limit_value)s MB.') def __call__(self, value): # get the file size as cleaned value cleaned = self.clean(value.size) params = {'limit_value': self.limit_value, 'show_value': cleaned, 'value': value} if self.compare(cleaned, self.limit_value * 1024 * 1024): # convert limit_value from MB to Bytes raise ValidationError(self.message, code=self.code, params=params)
und dann in Ihrem Modell zum Beispiel:
image = models.ImageField(verbose_name='Image', upload_to='images/', validators=[MaxSizeValidator(1)])
BEARBEITEN: Hier ist der Quellcode von
MaxValueValidator
für weitere Details zu dieser Arbeit.quelle
Ich möchte allen Leuten danken, die verschiedene Lösungen für dieses Problem bereitgestellt haben. Ich hatte zusätzliche Anforderungen, bei denen ich (a) vor der Übermittlung eine Überprüfung der Dateilänge in JavaScript durchführen wollte, (b) eine zweite Verteidigungslinie auf dem Server durchführen wollte
forms.py
, (c) alle fest codierten Bits einschließlich Endbenutzernachrichten beibehalten wollte informs.py
, (d) Ich wollte, dass ichviews.py
so wenig dateibezogenen Code wie möglich habe, und (d) lade die Dateiinformationen in meine Datenbank hoch, da dies kleine Dateien sind, die ich nur für angemeldete Benutzer bereitstellen und beimMeal
Modell sofort löschen möchte Elemente werden gelöscht (dh es reicht nicht aus, sie nur in / media / abzulegen).Zuerst das Modell:
class Meal(models.Model) : title = models.CharField(max_length=200) text = models.TextField() # Picture (you need content type to serve it properly) picture = models.BinaryField(null=True, editable=True) content_type = models.CharField(max_length=256, null=True, help_text='The MIMEType of the file') # Shows up in the admin list def __str__(self): return self.title
Dann benötigen Sie ein Formular, das sowohl die In-Server-Validierung als auch die Konvertierung vor
InMemoryUploadedFile
dem Speichern von nachbytes
ausführt und dasContent-Type
für die spätere Bereitstellung abruft.class CreateForm(forms.ModelForm): max_upload_limit = 2 * 1024 * 1024 max_upload_limit_text = str(max_upload_limit) # A more natural size would be nice upload_field_name = 'picture' # Call this 'picture' so it gets copied from the form to the in-memory model picture = forms.FileField(required=False, label='File to Upload <= '+max_upload_limit_text) class Meta: model = Meal fields = ['title', 'text', 'picture'] def clean(self) : # Reject if the file is too large cleaned_data = super().clean() pic = cleaned_data.get('picture') if pic is None : return if len(pic) > self.max_upload_limit: self.add_error('picture', "File must be < "+self.max_upload_limit_text+" bytes") def save(self, commit=True) : # Convert uploaded files to bytes instance = super(CreateForm, self).save(commit=False) f = instance.picture # Make a copy if isinstance(f, InMemoryUploadedFile): bytearr = f.read(); instance.content_type = f.content_type instance.picture = bytearr # Overwrite with the actual image data if commit: instance.save() return instance
Fügen Sie in der Vorlage diesen Code hinzu (angepasst an eine vorherige Antwort):
<script> $("#upload_form").submit(function() { if (window.File && window.FileReader && window.FileList && window.Blob) { var file = $('#id_{{ form.upload_field_name }}')[0].files[0]; if (file && file.size > {{ form.max_upload_limit }} ) { alert("File " + file.name + " of type " + file.type + " must be < {{ form.max_upload_limit_text }}"); return false; } } }); </script>
Hier ist der Ansichtscode, der sowohl Erstellen als auch Aktualisieren behandelt:
class MealFormView(LoginRequiredMixin, View): template = 'meal_form.html' success_url = reverse_lazy('meals') def get(self, request, pk=None) : if not pk : form = CreateForm() else: meal = get_object_or_404(Meal, id=pk, owner=self.request.user) form = CreateForm(instance=meal) ctx = { 'form': form } return render(request, self.template, ctx) def post(self, request, pk=None) : if not pk: form = CreateForm(request.POST, request.FILES or None) else: meal = get_object_or_404(Meal, id=pk, owner=self.request.user) form = CreateForm(request.POST, request.FILES or None, instance=meal) if not form.is_valid() : ctx = {'form' : form} return render(request, self.template, ctx) form.save() return redirect(self.success_url)
Dies ist eine sehr einfache Ansicht, die sicherstellt, dass request.FILES während der Erstellung der Instanz übergeben wird. Sie könnten fast die generische CreateView verwenden, wenn sie (a) mein Formular verwenden und (b) request.files beim Erstellen der Modellinstanz übergeben würde.
Um den Aufwand abzuschließen, habe ich die folgende einfache Ansicht, um die Datei zu streamen:
def stream_file(request, pk) : meal = get_object_or_404(Meal, id=pk) response = HttpResponse() response['Content-Type'] = meal.content_type response['Content-Length'] = len(meal.picture) response.write(meal.picture) return response
Dies erzwingt nicht, dass Benutzer angemeldet werden, aber ich habe dies weggelassen, da diese Antwort bereits zu lang ist.
quelle
In meinem Fall begrenzt Django die Größe der Upload-Datei. Wenn Sie die folgenden Einstellungen hinzufügen, werden die Einschränkungen aufgehoben.
# allow upload big file DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 15 # 15M FILE_UPLOAD_MAX_MEMORY_SIZE = DATA_UPLOAD_MAX_MEMORY_SIZE
quelle
from django.forms.utils import ErrorList class Mymodelform(forms.ModelForm): class Meta: model = Mymodel fields = '__all__' def clean(self):image = self.cleaned_data.get('image') # 5MB - 5242880 if org_image._size > 5242880: self._errors["image"] = ErrorList([u"Image too heavy."])
quelle
Sie können Djangos erweitern
MaxValueValidator
und überschreibenclean()
, um die Dateigröße zurückzugeben:from django.core.validators import MaxValueValidator from django.utils.deconstruct import deconstructible from django.utils.translation import ugettext_lazy as _ @deconstructible class MaxKibFileSizeValidator(MaxValueValidator): message = _('File size %(show_value)d KiB exceeds maximum file size of %(limit_value)d KiB.') def clean(self, filefield) -> float: return filefield.file.size / 1024
quelle