Django/Nginx - 服务超过一定大小的媒体文件时出现错误 403 禁止
Django/Nginx - Error 403 Forbidden when serving media files over some size
当用户上传图片时,它存储在项目目录下的media
文件夹中。问题是,当他们想在网站上看到它时,nginx return 403 Forbidden
图像超过大约 3 Mb 时出错。
我设置nginx.conf
client_max_body_size
为8M
http {
##
# Basic Settings
##
client_max_body_size 8M;
...
并且已经在 settings.py
中更改了内存大小:
FILE_UPLOAD_MAX_MEMORY_SIZE = 8388608
当我上传 3 MB 以下的图片时,没有问题,如果我上传超过 3 MB 的图片,我可以在 media
文件夹中看到它,但出现错误而不是提供图片:
GET https://example.com/media/images/dom.jpg 403 (Forbidden)
我注意到 3 MB 以下的文件具有不同的权限:
-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
你知道问题出在哪里吗?
编辑:
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
我们使用https://github.com/blueimp/jQuery-File-Upload插件上传图片。
$(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();
}
});
By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory.
更具体地说,这意味着较小的文件使用 MemoryFileUploadHandler while larger files use the TemporaryFileUploadHandler。后者使用 tempfile
创建一个只有用户才能访问的临时文件。
在完成所有表单和模型验证等所有操作后,实际保存由 FileSystemStorage._save
方法执行。此时,文件仍然是 TemporaryUploadedFile
或 InMemoryUploadedFile
,具体取决于其大小。
现在,TemporaryUploadedFile 是一个实际文件,由 tempfile
创建,仅具有用户权限。
save 方法做了一件聪明的事情:如果给定一个临时文件(即 if hasattr(content, 'temporary_file_path')
),它会移动它而不是复制它。这意味着它保留其仅限用户的权限,并且 www-data
.
无法读取
问题不会出现在 InMemoryUploadedFile 中,它将简单地使用进程拥有的任何默认权限(在您的情况下,read/write 用于用户和组)。
如何解决?
如果请求,存储对象可以设置权限。对于默认存储对象,您可以使用 FILE_UPLOAD_PERMISSIONS
进行设置。这里,
FILE_UPLOAD_PERMISSIONS=0o640
…应该可以解决问题。
(对于 django 用户是 R/W,对于 nginx 是只读的)
当用户上传图片时,它存储在项目目录下的media
文件夹中。问题是,当他们想在网站上看到它时,nginx return 403 Forbidden
图像超过大约 3 Mb 时出错。
我设置nginx.conf
client_max_body_size
为8M
http {
##
# Basic Settings
##
client_max_body_size 8M;
...
并且已经在 settings.py
中更改了内存大小:
FILE_UPLOAD_MAX_MEMORY_SIZE = 8388608
当我上传 3 MB 以下的图片时,没有问题,如果我上传超过 3 MB 的图片,我可以在 media
文件夹中看到它,但出现错误而不是提供图片:
GET https://example.com/media/images/dom.jpg 403 (Forbidden)
我注意到 3 MB 以下的文件具有不同的权限:
-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
你知道问题出在哪里吗?
编辑:
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
我们使用https://github.com/blueimp/jQuery-File-Upload插件上传图片。
$(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();
}
});
By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory.
更具体地说,这意味着较小的文件使用 MemoryFileUploadHandler while larger files use the TemporaryFileUploadHandler。后者使用 tempfile
创建一个只有用户才能访问的临时文件。
在完成所有表单和模型验证等所有操作后,实际保存由 FileSystemStorage._save
方法执行。此时,文件仍然是 TemporaryUploadedFile
或 InMemoryUploadedFile
,具体取决于其大小。
现在,TemporaryUploadedFile 是一个实际文件,由 tempfile
创建,仅具有用户权限。
save 方法做了一件聪明的事情:如果给定一个临时文件(即 if hasattr(content, 'temporary_file_path')
),它会移动它而不是复制它。这意味着它保留其仅限用户的权限,并且 www-data
.
问题不会出现在 InMemoryUploadedFile 中,它将简单地使用进程拥有的任何默认权限(在您的情况下,read/write 用于用户和组)。
如何解决?
如果请求,存储对象可以设置权限。对于默认存储对象,您可以使用 FILE_UPLOAD_PERMISSIONS
进行设置。这里,
FILE_UPLOAD_PERMISSIONS=0o640
…应该可以解决问题。
(对于 django 用户是 R/W,对于 nginx 是只读的)