When user uploads image, it is stored in media
folder inside project directory. The problem is that when they want to see it on the website, nginx return 403 Forbidden
error for images over approximately 3 Mb.
I set nginx.conf
client_max_body_size
to 8M
http {
##
# Basic Settings
##
client_max_body_size 8M;
...
And already changed memory size in settings.py
:
FILE_UPLOAD_MAX_MEMORY_SIZE = 8388608
When I upload an image under 3 MB, there are no problems, if I upload image over 3 MB, I can see it inside media
folder but the error is raised instead of serving image:
GET https://example.com/media/images/dom.jpg 403 (Forbidden)
I noticed that files under 3 MB have different permissions:
-rw-r--r-- 1 django www-data 4962 Jul 19 19:51 61682_3995232_IMG_01_0000.jpg.150x84_q85_crop.jpg
-rw-r--r-- 1 django www-data 1358541 Jul 20 09:32 byt.jpg
-rw------- 1 django www-data 3352841 Jul 20 09:32 dom.jpg
-rw-r--r-- 1 django www-data 5478 Jul 19 20:10 downloasd.jpeg.150x84_q85_crop.jpg
-rw-r--r-- 1 django www-data 3225 Jul 9 22:53 images.jpeg.100x56_q85_crop.jpg
-rw-r--r-- 1 django www-data 6132 Jul 19 20:00 NorthYorkHouse2.JPG.150x84_q85_crop.jpg
Do you know where is the problem?
EDIT:
VIEW
class NehnutelnostUploadImagesView(LoginRequiredMixin, ExclusiveMaklerDetailView, DetailView):
template_name = "nehnutelnosti/nehnutelnost_image_upload.html"
model = Nehnutelnost
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = ImageUploadForm(self.request.POST, self.request.FILES, nehnutelnost=self.object)
if form.is_valid():
nehnutelnost_image = form.save()
images_count = self.object.images.count()
data = {'is_valid': True, 'row_html': image_row_renderer(nehnutelnost_image, self.request),
'name': nehnutelnost_image.image.name, 'url': nehnutelnost_image.image.url,}
else:
images_count = self.object.images.count()
data = {'is_valid': False, 'errors': form.errors, 'images_count': images_count}
return JsonResponse(data)
def get_context_data(self, **kwargs):
context = super(NehnutelnostUploadImagesView, self).get_context_data(**kwargs)
context['images'] = self.object.images.all()
context['podorys'] = self.object.podorys
return context
We use https://github.com/blueimp/jQuery-File-Upload plugin to upload images.
$(function () {
$(".js-upload-photos").click(function () {
$("#fileupload").click();
});
$("#fileupload").fileupload({
dataType: 'json',
sequentialUploads: true, /* 1. SEND THE FILES ONE BY ONE */
start: function (e) { /* 2. WHEN THE UPLOADING PROCESS STARTS, SHOW THE MODAL */
$(".modal").modal().show();
},
stop: function (e) { /* 3. WHEN THE UPLOADING PROCESS FINALIZE, HIDE THE MODAL */
$(".modal").modal().hide();
$(".modal-backdrop").hide();
},
{# TODO Chrome bug?#}
progressall: function (e, data) { /* 4. UPDATE THE PROGRESS BAR */
var progress = parseInt(data.loaded / data.total * 100, 10);
var strProgress = progress + "%";
$(".progress-bar").css({"width": strProgress});
$(".progress-bar").text(strProgress);
},
done: function (e, data) {
if (data.result.is_valid) {
$(".gridly").prepend(
data.result.row_html
)
}
var message = data.result.message;
addMessage('success', message);
var errors = data.result.errors;
if (errors) {
$.each(errors, function (fieldname, error_messages) {
$.each(error_messages, function (_, message) {
addMessage('danger', message);
})
})
}
var images_count_span = $('#images_count');
var images_count = data.result.images_count;
images_count_span.text(' - ' + images_count);
makegrid();
}
});
From the documentation:
By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory.
In more concrete terms, it means smaller files use the MemoryFileUploadHandler while larger files use the TemporaryFileUploadHandler. The latter uses tempfile
to create a temporary file with user-only access.
After going through all form and model validation and everything, the actual saving is performed by FileSystemStorage._save
method. At this point, the file is still either a TemporaryUploadedFile
or a InMemoryUploadedFile
depending on its size.
Now, a TemporaryUploadedFile is an actual file, created by tempfile
, with user-only permissions.
The save method does the smart thing: if given a temporary file (namely, if hasattr(content, 'temporary_file_path')
), it moves it instead of copying it. This means it keeps its user-only permissions and remains unreadable by www-data
.
The problem doesn't show up with InMemoryUploadedFile, which will simply use whatever default permissions the process has (in your case, read/write for both user and group).
How to fix?
The storage object can set the permissions if so requested. For the default storage object, you can set this using FILE_UPLOAD_PERMISSIONS
. Here,
FILE_UPLOAD_PERMISSIONS=0o640
…should do the trick.
(that's R/W for django user and read-only for nginx)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With