django-3つの方法でファイルのダウンロードを実現


実際のプロジェクトでは、excel、pdf、ファイルのダウンロードなどのダウンロード機能が必要になることが多い.もちろん、Webサービスを使用してnginxなどのダウンロードに使用できるリソースサーバを自分で構築することができます.ここでは、djangoのファイルのダウンロードについて説明します.
ここでダウンロードしたファイルをプロジェクトmediaディレクトリに保存します.もちろん実際にはそうはいきません.
方法一:HttpResponseを使用する
import os
from django.http import HttpResponse, Http404


def media_file_download(request, file_path):
    with open(file_path, 'rb') as f:
        try:
            response = HttpResponse(f)
            response['content_type'] = "application/octet-stream"
            response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
            return response
        except Exception:
            raise Http404

HttpResponseには大きな弊害があり、ファイルを読み取り、メモリをロードしてから出力するのが原理です.ダウンロードファイルが大きい場合、このメソッドは多くのメモリを消費します.大きなファイルをダウンロードする場合、DjangoはStreamingHttpResponseとFileResponseの方法をお勧めします.この2つの方法は、サーバメモリにロードしないで、ダウンロードファイルのバッチ(Chunks)をユーザーのローカルディスクに書き込みます.
方法2:StreamingHttpResponseの使用
import os
from django.http import HttpResponse, Http404, StreamingHttpResponse

def stream_http_download(request, file_path):
    try:
        response = StreamingHttpResponse(open(file_path, 'rb'))
        response['content_type'] = "application/octet-stream"
        response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
        return response
    except Exception:
        raise Http404

方式3:FileResponseの使用
import os
from django.http import HttpResponse, Http404, FileResponse


def file_response_download1(request, file_path):
    try:
        response = FileResponse(open(file_path, 'rb'))
        response['content_type'] = "application/octet-stream"
        response['Content-Disposition'] = 'attachment; filename=' + os.path.basename(file_path)
        return response
    except Exception:
        raise Http404


ファイル名の中国語の文字化けしの問題
ここでは英語のファイル名を使用し、ブラウザは正常に表示されますが、中国語を使用すると、 .xlsなどのデフォルトのファイル名、またはutf-8符号化を使用した場合は、文字化けしています.解決策は次のとおりです.
response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(name))

ファイルのプライベート化の2つの方法
ログインしたユーザーだけがファイルを表示およびダウンロードできるようにするには、2つの方法があります.ここでは、アイデアのみを提供します.
  • アップロードファイルはmediaフォルダに置かれ、ファイル名は長いランダム文字列ネーミング(uuid)を使用しており、ユーザーがファイル名に基づいて何のファイルなのか推測できない.ビューとテンプレートにユーザーがログインしているかどうかを確認し、ログインまたは権限検証によって特定のurlを表示します.-シンプルで実現しやすく、セキュリティは高くありませんが、一般的なプロジェクトでは十分です.
  • アップロードファイルは非メディアフォルダに置かれており、Djangoはメディアフォルダ内の各ファイルに独立したurlリソースを作成するだけであるため、ユーザーは具体的なファイルアドレスを知っていてもアクセスできません.ビューとテンプレートで、ユーザーがログインしているかどうかを確認したり、ログインしたり、権限検証後に自分で作成したダウンロード方法でファイルをダウンロードしたりします.-セキュリティは高いが、実装は比較的複雑である.

  • 個人ダウンロードドキュメントviewビューコード
    from django.views import View
    from django.conf import settings
    from django.http import FileResponse,Http404
    from django.utils.encoding import escape_uri_path
    
    from .models import Doc
    
    import requests
    import logging
    
    logger = logging.getLogger('django')
    class Download(View):
        """
              doc id,         
        """
        def get(self,request,doc_id):
            doc = Doc.objects.only('file_url').filter(is_delete=False,id = doc_id).first()
            if doc:
                doc_url = doc.file_url
                doc_url = settings.ITEM_DOMAIN_PORT + doc_url
                try:
                    res = FileResponse(requests.get(doc_url,stream = True))
                except Exception as e:
                    logger.info('      :{}'.format(e))
                    raise Http404('      ')
                file_end = doc_url.split('.')[-1]
                if not file_end:
                    raise Http404('      ')
                else:
                    file_end = file_end.lower()
                if file_end == "pdf":
                    res["Content-type"] = "application/pdf"
                elif file_end == "zip":
                    res["Content-type"] = "application/zip"
                elif file_end == "doc":
                    res["Content-type"] = "application/msword"
                elif file_end == "xls":
                    res["Content-type"] = "application/vnd.ms-excel"
                elif file_end == "docx":
                    res["Content-type"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                elif file_end == "ppt":
                    res["Content-type"] = "application/vnd.ms-powerpoint"
                elif file_end == "pptx":
                    res["Content-type"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
    
                else:
                    raise Http404("       !")
                doc_filename = escape_uri_path(doc_url.split('/')[-1])
                # http1.1     
                #    inline,     
                # attachment         
                res["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(doc_filename)
                return res
    
            else:
                raise Http404("     !")