[python] Django가 다운로드 가능한 파일을 제공하게 함

사이트의 사용자가 경로가 가려진 파일을 다운로드하여 직접 다운로드 할 수 없도록하고 싶습니다.

예를 들어 URL을 다음과 같이 설정하고 싶습니다. http://example.com/download/?f=somefile.txt

그리고 서버에서 다운로드 가능한 모든 파일이 폴더에 있음을 알고 있습니다 /home/user/files/.

URL을 찾고 표시하려고 시도하는 대신 Django가 해당 파일을 다운로드 할 수 있도록하는 방법이 있습니까?



답변

“두 세계의 최고”를 위해 S.Lott의 솔루션을 xsendfile 모듈 과 결합 할 수 있습니다 . django는 파일 (또는 파일 자체)의 경로를 생성하지만 실제 파일 제공은 Apache / Lighttpd에 의해 처리됩니다. mod_xsendfile을 설정하면 뷰와 통합하는 데 몇 줄의 코드가 필요합니다.

from django.utils.encoding import smart_str

response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response

물론 이것은 서버를 제어하거나 호스팅 회사가 이미 mod_xsendfile을 설정 한 경우에만 작동합니다.

편집하다:

django 1.7에서는 mimetype이 content_type으로 바뀝니다.

response = HttpResponse(content_type='application/force-download')  

편집 :
들어 nginx확인 그것을 사용하는 X-Accel-Redirect대신 apacheX-sendfile을 헤더.


답변

“다운로드”는 단순히 HTTP 헤더 변경입니다.

다운로드로 응답하는 방법 은 http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment 를 참조 하십시오. .

에 대한 하나의 URL 정의 만 필요합니다 "/download".

요청 GET또는 POST사전에 "f=somefile.txt"정보가 있습니다.

뷰 함수는 단순히 기본 경로를 ” f“값 과 병합 하고 파일을 열고 응답 객체를 만들고 반환합니다. 코드는 12 줄 미만이어야합니다.


답변

매우 간단 하지만 효율적이지 않거나 확장 가능하지 않은 솔루션의 경우 내장 장고 serve보기를 사용할 수 있습니다. 이것은 빠른 프로토 타입이나 일회성 작업에는 탁월하지만이 질문에서 언급했듯이 프로덕션에서는 아파치 나 nginx와 같은 것을 사용해야합니다.

from django.views.static import serve
filepath = '/some/path/to/local/file.txt'
return serve(request, os.path.basename(filepath), os.path.dirname(filepath))


답변

S.Lott는 “양호한”/ 간단한 솔루션을, elo80ka는 “최고”/ 효율적인 솔루션을 제공합니다. 다음은 “더 나은”/ 중간 솔루션입니다. 서버 설정은 없지만 순진한 수정보다 큰 파일에 더 효율적입니다.

http://djangosnippets.org/snippets/365/

기본적으로 장고는 여전히 파일 서비스를 처리하지만 한 번에 모든 것을 메모리에로드하지 않습니다. 이를 통해 서버는 메모리 사용량을 늘리지 않고 (느리게) 큰 파일을 제공 할 수 있습니다.

다시 말하지만 S.Lott의 X-SendFile은 더 큰 파일에 더 좋습니다. 그러나 당신이 그것을 귀찮게하고 싶지 않거나 원하지 않는다면,이 중간 솔루션은 번거 로움없이 더 나은 효율성을 얻을 것입니다.


답변

@Rocketmonkeys 솔루션을 시도했지만 다운로드 한 파일이 * .bin으로 저장되고 임의의 이름이 지정되었습니다. 물론 좋지 않습니다. @ elo80ka에서 다른 줄을 추가하면 문제가 해결되었습니다.
다음은 지금 사용중인 코드입니다.

from wsgiref.util import FileWrapper
from django.http import HttpResponse

filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg"
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename)
response['Content-Length'] = os.path.getsize(filename)
return response

이제 / media 나 / public_html이 아닌 개인 디렉토리에 파일을 저장하고 django를 통해 특정 사용자 또는 특정 상황에 노출시킬 수 있습니다.
도움이 되길 바랍니다.

@ elo80ka, @ S.Lott 및 @Rocketmonkeys 덕분에 답변에 대한 완벽한 솔루션을 얻었습니다 =)


답변

FileResponse를 언급하는 것만Django 1.10에서 사용 가능한 객체를

편집 : Django를 통해 파일을 스트리밍하는 쉬운 방법을 검색하는 동안 내 대답에 부딪 쳤으므로 여기에 더 완벽한 예가 있습니다. FileField 이름이imported_file

views.py

from django.views.generic.detail import DetailView
from django.http import FileResponse
class BaseFileDownloadView(DetailView):
  def get(self, request, *args, **kwargs):
    filename=self.kwargs.get('filename', None)
    if filename is None:
      raise ValueError("Found empty filename")
    some_file = self.model.objects.get(imported_file=filename)
    response = FileResponse(some_file.imported_file, content_type="text/csv")
    # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files
    response['Content-Disposition'] = 'attachment; filename="%s"'%filename
    return response

class SomeFileDownloadView(BaseFileDownloadView):
    model = SomeModel

urls.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...


답변

위에서 mod_xsendfile 메소드는 파일 이름에 ASCII가 아닌 문자를 허용하지 않습니다.

이러한 이유로 이름이 URL로 인코딩되고 추가 헤더가있는 한 모든 파일을 보낼 수있는 mod_xsendfile에 사용할 수있는 패치가 있습니다.

X-SendFile-Encoding: url

전송됩니다.

http://ben.timby.com/?p=149