68👍
This code might help:
# 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
Taken from: Django Snippets – Validate by file content type and size
108👍
You can use this snippet formatChecker. What it does is
-
it lets you specify what file formats are allowed to be uploaded.
-
and lets you set the limit of file size of the file to be uploaded.
First. Create a file named formatChecker.py inside the app where the you have the model that has the FileField that you want to accept a certain file type.
This is your 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
Second. In your models.py, add this:
from formatChecker import ContentTypeRestrictedFileField
Then instead of using ‘FileField’, use this ‘ContentTypeRestrictedFileField’.
Example:
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)
You can change the value of ‘max_upload_size’ to the limit of file size that you want. You can also change the values inside the list of ‘content_types’ to the file types that you want to accept.
- [Django]-How to merge consecutive database migrations in django 1.9+?
- [Django]-Change a form value before validation in Django form
- [Django]-Setting DEBUG = False causes 500 Error
100👍
another solution is using validators
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.')
then in your form with the File field you have something like this
image = forms.FileField(required=False, validators=[file_size])
- [Django]-How to See if a String Contains Another String in Django Template
- [Django]-Update all models at once in Django
- [Django]-Django: Group by date (day, month, year)
26👍
I believe that django form receives file only after it was uploaded completely.That’s why if somebody uploads 2Gb file, you’re much better off with web-server checking for size on-the-fly.
See this mail thread for more info.
- [Django]-Get last record in a queryset
- [Django]-Jquery template tags conflict with Django template!
- [Django]-Django filter many-to-many with contains
14👍
Server side
My favourite method of checking whether a file is too big server-side is ifedapo olarewaju’s answer using a validator.
Client side
The problem with only having server-side validation is that the validation only happens after the upload is complete. Imagine, uploading a huge file, waiting for ages, only to be told afterwards that the file is too big. Wouldn’t it be nicer if the browser could let me know beforehand that the file is too big?
Well, there is a way to this client side, using HTML5 File API!
Here’s the required Javascript (depending on 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;
}
}
});
Of course, you still need server-side validation, to protect against malicious input, and users that don’t have Javascript enabled.
- [Django]-Django 1.7 – makemigrations not detecting changes
- [Django]-Uwsgi installation error in windows 7
- [Django]-Can we append to a {% block %} rather than overwrite?
10👍
Just a short note on the snippet that was included in this thread:
Take a look at this snippet:
http://www.djangosnippets.org/snippets/1303/
It was very usefull, however it’s including a few minor mistakes. More robust code should look like this:
# 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
There are just a few improvements:
First of all I’m detecting if the file field is empty (None) – without it, Django will cast an exception in web browser.
Next is type casting in int(settings.MAX_UPLOAD_SIZE), because that setting value is a string. Strings cannot be used for comparing with numbers.
Last but not least, the unicode ‘u’ prefix in ValidationError function.
Thank you very much for this snippet!
- [Django]-Django template can't see CSS files
- [Django]-What is choice_set in this Django app tutorial?
- [Django]-How to use 'select_related' with get_object_or_404?
9👍
In my case, django limit the upload file size.
Add the following settings will remove restriction.
# allow upload big file
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024 * 15 # 15M
FILE_UPLOAD_MAX_MEMORY_SIZE = DATA_UPLOAD_MAX_MEMORY_SIZE
- [Django]-URL-parameters and logic in Django class-based views (TemplateView)
- [Django]-Difference between filter with multiple arguments and chain filter in django
- [Django]-Django Rest Framework remove csrf
8👍
If someone is looking for a form FileField
variant of @angelo solution then here it is
from 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
Then create a form as
class ImageUploadForm(forms.Form):
"""Image upload form."""
db_image = RestrictedFileField(content_types=['image/png', 'image/jpeg'],
max_upload_size=5242880)
- [Django]-Images from ImageField in Django don't load in template
- [Django]-How to delete project in django
- [Django]-Best practice for Django project working directory structure
6👍
Another elegant solution with validators that does not hard-code the max file size is by using a class based validator:
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)
and then, in your model, for example:
image = models.ImageField(verbose_name='Image', upload_to='images/', validators=[MaxSizeValidator(1)])
EDIT: here is the source code of MaxValueValidator
for more details on this works.
- [Django]-Django custom management commands: AttributeError: 'module' object has no attribute 'Command'
- [Django]-How do I convert a Django QuerySet into list of dicts?
- [Django]-Django queries: how to filter objects to exclude id which is in a list?
5👍
I want to thank all the folks who have provided various different solutions to this problem. I had additional requirements where I wanted to (a) do file length validation in JavaScript before submission, (b) do a second line of defense in-server validation in the forms.py
, (c) keep all hard-coded bits including end-user messages in forms.py
, (d) I wanted my views.py
have as little file-related code as possible, and (d) upload the file information to my database since these are small files that I want to only serve to logged in users and instantly delete when the Meal
model items are deleted (i.e. so just dropping them in /media/ is not sufficient).
First the model:
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
Then you need a form that both does the in-server validation and the pre-save conversion from InMemoryUploadedFile
to bytes
and grabbing the Content-Type
for later serving.
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
In the template, add this code (adapted from a previous answer):
<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>
Lately I am moving away from JQuery where I can, so the above client side code to check file size in Vanilla JS is:
<script>
document.getElementById("upload_form").addEventListener("submit", function() {
console.log('Checking file size');
if (window.File && window.FileReader && window.FileList && window.Blob) {
var file = document.getElementById('id_{{ form.upload_field_name }}').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>
Here is the view code that handles both Create and Update:
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)
This is a very simple view that makes sure that request.FILES is passed in
during the creation of the instance. You could almost use the generic CreateView if it would (a) use my form and (b) pass request.files when making the model instance.
Just to complete the effort, I have the following simple view to stream the file:
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
This does not force users to be logged in, but I omitted that since this answer is already too long.
- [Django]-Django – Rotating File Handler stuck when file is equal to maxBytes
- [Django]-Django signals vs. overriding save method
- [Django]-Django: "projects" vs "apps"
1👍
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."])
- [Django]-Django models avoid duplicates
- [Django]-How exactly do Django content types work?
- [Django]-Django-allauth: Linking multiple social accounts to a single user
1👍
You can extend Django’s MaxValueValidator
and overwrite it’s clean()
to return the file size:
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
- [Django]-Unique BooleanField value in Django?
- [Django]-Django content-type : how do I get an object?
- [Django]-Django-social-auth django-registration and django-profiles — together
0👍
I struggled with limiting both the file type and size of uploaded documents. So here is my final solution (partially based on one of the solutions above):
- I created a
filechecker.py
and added :
import os
from django.core.exceptions import ValidationError
def file_size(value):
limit = 1048576
if value.size > limit:
raise ValidationError('File too large. Size should not exceed 1 MB.')
def file_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.pdf', '.doc', '.docx']
if not ext in valid_extensions:
raise ValidationError('Unsupported file type. Only Pdf and MsWord files are allowed.')
- Imported the functions in
forms.py
and used them in the list of validators for mycv
field:
# ....other imports
from .filechecker import file_size, file_extension
class JobApplicationForm(forms.ModelForm):
cv = forms.FileField(required=True, validators=[file_size, file_extension] )
class Meta:
model = jobApplicant
fields = ["email", "cv"]
- [Django]-Django: Open uploaded file while still in memory; In the Form Clean method?
- [Django]-Django.db.migrations.exceptions.InconsistentMigrationHistory
- [Django]-What is the difference between cached_property in Django vs. Python's functools?