사이트의 사용자가 경로가 가려진 파일을 다운로드하여 직접 다운로드 할 수 없도록하고 싶습니다.
예를 들어 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
대신 apache
X-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
전송됩니다.
