web-dev-qa-db-ja.com

Django / Nginx-エラー403あるサイズを超えるメディアファイルを提供する場合は禁止

ユーザーが画像をアップロードすると、プロジェクトディレクトリ内のmediaフォルダに保存されます。問題は、ウェブサイトで見たいときに、nginxが約3Mbを超える画像に対して403 Forbiddenエラーを返すことです。

nginx.confclient_max_body_sizeを8Mに設定しました

http {

        ##
        # Basic Settings
        ##
        client_max_body_size 8M;
     ...

そして、settings.pyのメモリサイズはすでに変更されています。

FILE_UPLOAD_MAX_MEMORY_SIZE = 8388608

3 MB未満の画像をアップロードする場合、問題はありません。3MBを超える画像をアップロードすると、mediaフォルダー内に表示されますが、画像を提供する代わりにエラーが発生します。

GET https://example.com/media/images/dom.jpg 403 (Forbidden)

3MB未満のファイルには異なる権限があることに気づきました。

-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

問題がどこにあるか知っていますか?

編集:

[〜#〜]表示[〜#〜]

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();

                }

            });
8
Milano

ドキュメント から:

デフォルトでは、アップロードされたファイルが2.5メガバイトよりも小さい場合、Djangoはアップロードの内容全体をメモリに保持します。

より具体的には、小さいファイルは MemoryFileUploadHandler を使用し、大きいファイルは TemporaryFileUploadHandler を使用することを意味します。後者はtempfileを使用して、ユーザーのみのアクセス権を持つ一時ファイルを作成します。

すべてのフォームとモデルの検証およびすべてを実行した後、実際の保存は_FileSystemStorage._save_メソッドによって実行されます。この時点では、ファイルはサイズに応じてTemporaryUploadedFileまたはInMemoryUploadedFileのいずれかになります。

現在、TemporaryUploadedFileは、tempfileによって作成された実際のファイルであり、ユーザーのみの権限があります。

Saveメソッドは賢いことをします:一時ファイル(つまり、if hasattr(content, 'temporary_file_path'))が与えられると、それをコピーする代わりに移動します。これは、ユーザーのみの権限を保持し、_www-data_によって読み取り不能のままであることを意味します。

この問題は、プロセスが持つデフォルトのアクセス許可(この場合、ユーザーとグループの両方の読み取り/書き込み)を使用するInMemoryUploadedFileでは発生しません。

修正方法は?

ストレージオブジェクトは、要求された場合にアクセス許可を設定できます。デフォルトのストレージオブジェクトの場合、これは _FILE_UPLOAD_PERMISSIONS_ を使用して設定できます。ここに、

_FILE_UPLOAD_PERMISSIONS=0o640
_

…トリックを行う必要があります。

(これはDjangoユーザーのR/Wであり、nginxの読み取り専用です)

14
spectras