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


245

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

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

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

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


2
왜 단순히 아파치를 사용하여 이것을하지 않습니까? Apache는 Django보다 정적 콘텐츠를 더 빠르고 간단하게 제공합니다.
S.Lott

22
Django에 기반을 둔 권한없이 파일에 액세스 할 수 있기를 원하지 않기 때문에 Apache를 사용하지 않습니다.
damon

3
사용자 권한을 고려하려면 Django의보기를 통해 파일을 제공해야합니다
Łukasz

127
정확히, 이것이 내가이 질문을하는 이유입니다.
damon

답변:


189

"두 세계의 최고"를 위해 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을 헤더.


6
파일 이름 또는 path_to_file에 "ä"또는 "ö"와 같은 ASCII 이외의 문자가 포함 된 경우 smart_str아파치 모듈 X-Sendfile이 smart_str 인코딩 된 문자열을 디코딩 할 수 없으므로 의도 한대로 작동하지 않습니다. 따라서 "Örinää.mp3"파일은 제공 할 수 없습니다. 그리고 smart_str을 생략하면 Django 자체는 모든 헤더 가 보내기 전에 ASCII 형식으로 인코딩 되므로 ASCII 인코딩 오류가 발생 합니다. 이 문제를 피할 수있는 유일한 방법은 X-sendfile 파일 이름을 ascii로만 구성된 파일 이름으로 줄이는 것입니다.
Ciantic

3
더 명확하게 : S.Lott는 간단한 설정으로 django에서 직접 파일을 제공하며 다른 설정이 필요하지 않습니다. elo80ka는 웹 서버가 정적 파일을 처리하고 django가 필요없는 더 효율적인 예를 가지고 있습니다. 후자는 더 나은 성능을 갖지만 더 많은 설정이 필요할 수 있습니다. 둘 다 장소가 있습니다.
rocketmonkeys

1
@Ciantic, 인코딩 문제에 대한 해결책으로 보이는 것에 대한 btimby의 답변을 참조하십시오.
mlissner 2012

이 솔루션은 다음 웹 서버 구성에서 작동합니까? 백엔드 : 2 개 이상의 Apache + mod_wsgi 개별 (VPS) 서버가 서로 복제하도록 설정되었습니다. 프론트 엔드 : 라운드 로빈을 수행하는 업스트림로드 밸런싱을 사용하는 1 개의 nginx 프록시 (VPS) 서버.
Daniel

12
마임은 장고 1.7 콘텐츠 _로 대체
ismailsunni

88

"다운로드"는 단순히 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 줄 미만이어야합니다.


49
이것은 본질적으로 정확한 (간단한) 대답이지만 한 가지주의-파일 이름을 매개 변수로 전달하면 사용자가 잠재적으로 모든 파일을 다운로드 할 수 있음을 의미 합니다 (예 : "f = / etc / passwd"를 전달하면 어떻게됩니까?) 이를 방지하는 데 도움이되는 것 (사용자 권한 등)이지만이 명백하지만 일반적인 보안 위험에주의하십시오. 기본적으로 유효성 검사 입력의 하위 집합입니다. 파일 이름을 뷰에 전달하면 해당 뷰에서 파일 이름을 확인하십시오!
rocketmonkeys

9
아주 간단한 이 보안 문제에 대한 수정 사항 :filepath = filepath.replace('..', '').replace('/', '')
duality_

7
테이블을 사용하여 파일 정보를 다운로드 할 수있는 사용자를 포함하여 파일 정보를 저장하는 경우 파일 이름이 아닌 기본 키만 보내면 앱에서 수행 할 작업을 결정합니다.
Edward Newell

30

매우 간단 하지만 효율적이지 않거나 확장 가능하지 않은 솔루션의 경우 내장 장고 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))

또한 Windows에서 테스트를위한 대체를 제공하는 데 매우 유용합니다.
아미르 알리 악바리

저는 데스크탑 클라이언트와 같은 종류의 작업을 위해 독립형 장고 프로젝트를 수행하고 있으며 완벽하게 작동했습니다. 감사!
daigorocub

1
왜 효율적이지 않습니까?
02 분

2
@zinking 파일은 일반적으로 django 프로세스가 아닌 아파치와 같은 것을 통해 제공되어야하기 때문에
Cory

1
여기서 우리는 어떤 종류의 성능 단점을 이야기합니까? 파일이 django를 통해 제공되는 경우 RAM 또는 일종의 파일로로드됩니까? 장고가 왜 nginx와 같은 효율로 서비스를 제공 할 수 없습니까?
Gershom

27

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

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

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

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


4
그 스 니펫은 좋지 않습니다. 스니핑은 django.core.servers.httpbase문서화되지 않은 개인 모듈 에 의존 하는데,이 코드는 맨 처음 생성 된 이후 파일에있는 " DO N'T USE FOR PRODUCTION USE !!! " 코드 상단에 큰 경고 표시 가 있습니다. 어쨌든 이 스 니펫이 의존 하는 기능은 django 1.9에서 제거되었습니다. FileWrapper
eykanal

16

@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 덕분에 답변에 대한 완벽한 솔루션을 얻었습니다 =)


1
고마워요, 이것은 내가 찾던 것입니다!
ihatecache

1
파일 이름 filename="%s"에 공백이있는 문제를 피하려면 Content-Disposition 헤더에서 파일 이름 주위에 큰 따옴표를 추가하십시오 . 참고 : 공간이 다운로드시립니다와 파일 이름 , 어떻게 HTTP에 내용 - 처리 헤더의 파일 이름 매개 변수를 인코딩하는 방법?
Christian Long

1
귀하의 솔루션이 저에게 효과적입니다. 그러나 파일에 대해 "잘못된 시작 바이트 ..."오류가 발생했습니다. 그것을 해결FileWrapper(open(path.abspath(file_name), 'rb'))
Mark Mishyn

FileWrapper장고 1.9 이후로 제거되었습니다
freethebees

사용 가능from wsgiref.util import FileWrapper
Kriss

15

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'),
...

1
대단히 감사합니다! 이진 파일을 다운로드하는 가장 간단한 솔루션이며 작동합니다.
Julia Zhao

13

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

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

X-SendFile-Encoding: url

전송됩니다.

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


패치는 이제 corer 라이브러리로 접 힙니다.
mlissner 2019

7

시험: https://pypi.python.org/pypi/django-sendfile/

"Django가 권한 등을 확인한 후 파일 업로드를 웹 서버 (예 : mod_xsendfile을 사용하는 Apache)로 오프로드하는 데 대한 매력"


2
(1 년 전) 내 개인 포크에는 원래의 저장소가 아직 포함되지 않은 대체 아파치 파일을 제공했습니다.
로베르토 로사리오

왜 링크를 제거 했습니까?
kiok46

@ kiok46 Github 정책과 충돌합니다. 정식 주소를 가리 키도록 수정되었습니다.
Roberto Rosario

6

프로덕션 과 같이 apache또는 nginx프로덕션 환경에서 널리 사용되는 서버에서 제공 한 sendfile api를 사용해야합니다 . 몇 년 동안 나는 파일을 보호하기 위해이 서버의 sendfile api를 사용했습니다. 그런 다음 개발 및 생산 목적 모두에 적합한이 용도로 간단한 미들웨어 기반 장고 앱을 만들었습니다 . 여기 에서 소스 코드에 액세스 할 수 있습니다 .
업데이트 : 새 버전의 python공급자에서는 FileResponse가능한 경우 django 를 사용하고 lighthttp, caddy에서 hiawatha에 이르기까지 많은 서버 구현에 대한 지원을 추가합니다.

용법

pip install django-fileprovider
  • 설정 에 fileprovider앱을 추가 INSTALLED_APPS하고
  • 추가 fileprovider.middleware.FileProviderMiddlewareMIDDLEWARE_CLASSES설정
  • 설정 을 프로덕션으로 FILEPROVIDER_NAME설정 nginx하거나 apache프로덕션 환경으로 설정하십시오. 기본적으로 python개발 목적입니다.

클래스 기반 또는 함수보기에서 응답 헤더 X-File값을 파일의 절대 경로로 설정 하십시오. 예를 들어

def hello(request):  
   // code to check or protect the file from unauthorized access
   response = HttpResponse()  
   response['X-File'] = '/absolute/path/to/file'  
   return response  

django-fileprovider 코드가 최소한의 수정 만 필요로하는 방식으로 실행됩니다.

Nginx 구성

직접 액세스로부터 파일을 보호하기 위해 구성을 다음과 같이 설정할 수 있습니다

 location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
 }

여기서는 내부 nginxURL /files/에만 액세스 하는 위치 URL을 설정합니다. 위의 구성을 사용하는 경우 X-File을 다음과 같이 설정할 수 있습니다.

response['X-File'] = '/files/filename.extension' 

nginx 구성 으로이 작업을 수행하면 파일이 보호되고 django에서 파일을 제어 할 수 있습니다 views


2

Django는 다른 서버를 사용하여 정적 미디어를 제공하는 것이 좋습니다 (동일한 컴퓨터에서 실행되는 다른 서버는 좋습니다). lighttp 와 같은 서버를 사용하는 것이 좋습니다. . .

이것은 설정이 매우 간단합니다. 하나. 요청에 따라 'somefile.txt'가 생성되면 (콘텐츠가 동적 임) django가 제공하도록 할 수 있습니다.

장고 문서-정적 파일


2
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 

0

살펴볼 또 다른 프로젝트 : http://readthedocs.org/docs/django-private-files/en/latest/usage.html 어렴풋이 보인다.

기본적으로 프로젝트는 mod_xsendfile 구성을 추상화하고 다음과 같은 작업을 수행 할 수 있습니다.

from django.db import models
from django.contrib.auth.models import User
from private_files import PrivateFileField

def is_owner(request, instance):
    return (not request.user.is_anonymous()) and request.user.is_authenticated and
                   instance.owner.pk = request.user.pk

class FileSubmission(models.Model):
    description = models.CharField("description", max_length = 200)
        owner = models.ForeignKey(User)
    uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner)

1
request.user.is_authenticated는 속성이 아닌 메소드입니다. is_authenticated는 is_anonymous의 역수이므로 request.user.is_anonymous ()가 아님은 request.user.is_authenticated ()와 동일합니다.
폭발

@explodes 최악의 경우, 그 코드는 다음과 같은 문서에서 옳습니다 django-private-files.
Armando Pérez Marqués



0

나는 이것에 대한 프로젝트를했다. 내 github repo를 볼 수 있습니다.

https://github.com/nishant-boro/django-rest-framework-download-expert

이 모듈은 Apache 모듈 Xsendfile을 사용하여 django rest 프레임 워크에서 다운로드 할 파일을 제공하는 간단한 방법을 제공합니다. 또한 특정 그룹에 속한 사용자에게만 다운로드를 제공하는 추가 기능이 있습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.