Vasyworksでログインを必要とするシステムについては、ユーザがアップロードしたメディアファイルは直接参照せずに表示用のビューアを通して参照するようにしています。DjangoのMEDIAのディレクトリをWEB公開せずにビューアを通して表示させることで、HTML内に記述されているメディア参照を直接URL指定してもログインしないと閲覧できないようにしています。 下記にサンプルコードを記載します。
■viewer/urls.py
"""
System Name: Vasyworks
Copyright (C) 2020 Yasuhiro Yamamoto
"""
from django.urls import path
from django.views.generic import TemplateView
from .views import *
urlpatterns = [
path('media/<path:file_url>', MediaViewerView.as_view(), name='viewer_media'),
path('', TemplateView.as_view(template_name='404.html'), name='viewer_index'),
]
■viewer/views.py
"""
System Name: Vasyworks
Copyright (C) 2020 Yasuhiro Yamamoto
"""
import os
import mimetypes
from django.conf import settings
from django.http import HttpResponse, Http404
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class MediaViewerView(View):
"""
メディアビューア
"""
@method_decorator(login_required)
def get(self, request, *args, **kwargs):
user = self.request.user
if not user:
raise Http404
file_url = kwargs.get('file_url')
if file_url:
file_path = os.path.join(settings.MEDIA_ROOT, file_url.replace('/', os.sep))
file_name = self.get_file_name(file_url)
file = None
try:
file = open(file_path, 'rb')
content_type = self.get_content_type(file_url)
response = HttpResponse(file.read(), content_type=content_type)
if self.is_attachment(content_type):
response['Content-Disposition'] = 'attachment; filename="{0}"'.format(file_name)
file.close()
return response
except:
if file:
file.close()
raise Http404
else:
raise Http404
@classmethod
def get_content_type(cls, url: str):
""" URLからContent-Typeを取得 """
ans = ''
if url:
mimetype = mimetypes.guess_type(url)
if mimetype:
ans = mimetype[0]
return ans
@classmethod
def get_file_name(cls, url: str):
ans = ''
if url:
ans = url.rsplit('/', 1)[1]
return ans
@classmethod
def is_attachment(cls, content_type):
""" 指定のContent_Typeがダウンロード対象ならTrue """
ans = True
if 'image/' in content_type:
ans = False
elif 'video/' in content_type:
ans = False
elif 'application/pdf' in content_type:
ans = False
return ans
urls.pyでパラメータのパスコンバータに「path」を指定してファイルパスを受け取るように指定しています。ファイルパスにはDjangoのMEDIAルート(/media/)からのパスを区切り文字に「/」を使って指定します。ちなみにURLディスパッチャで使えるデフォルトのパスコンバータはDjango3.2のドキュメントには「str」「int」「slug」「uuid」「path」の5つがあると記載されています。
views.pyのMediaViewerViewクラスのgetメソッドでlogin_requiredを指定していますので、ログインを介さずに直接ビューアを参照した場合はログインを促されます。Httpサーバ設定でDjangoのmediaディレクトリのエイリアス指定を行わないようにするなど、mediaディレクトリへのURL直接参照ができないようにしておけば、メディアファイルへのアクセスをビューア経由に制限できるので、ログインを強要することができます。
また、このMediaViewerViewクラスでは画像、動画、PDF以外のファイルが対象の場合はContent-Dispositionにattachmentを設定してダウンロードを促すように指定しています。