Имея Django служить загружаемые файлы


Я хочу, чтобы пользователи на сайте могли загружать файлы, пути которых скрыты, поэтому они не могут быть загружены напрямую.

например, я бы хотел, чтобы URL-адрес был примерно таким:"http://example.com/download/?f=somefile.txt

и на сервере я знаю, что все загружаемые файлы находятся в папке "/home/user/files/".

есть ли способ заставить Django обслуживать этот файл для загрузки, а не пытаться найти URL и просмотреть чтобы показать его?

14 208

14 ответов:

для "лучшего из обоих миров" вы можете объединить решение С. Лотта с модуль 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 набор вверх.

EDIT:

mimetype заменяется content_type для django 1.7

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

EDIT: Ибо 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 строк кода.

S. Lott имеет "хорошее" / простое решение, а elo80ka имеет "лучшее"/эффективное решение. Вот "лучшее" / среднее решение - нет установки сервера, но более эффективно для больших файлов, чем наивное исправление:

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

в принципе, Django по-прежнему обрабатывает обслуживание файла, но не загружает все это в память сразу. Это позволяет вашему серверу (медленно) обслуживать большой файл без увеличения объема памяти использование.

опять же, X-SendFile S. Lott по-прежнему лучше для больших файлов. Но если вы не можете или не хотите беспокоиться об этом, то это среднее решение обеспечит вам лучшую эффективность без хлопот.

очень просто но не эффективный или масштабируемый решение, вы можете просто использовать встроенный django serve вид. Это отлично подходит для быстрых прототипов или одноразовой работы, но, как уже упоминалось в этом вопросе, вы должны использовать что-то вроде apache или 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))

пробовал решение @Rocketmonkeys, но загруженные файлы хранились как *.Бин и даны случайные имена. Конечно, это не нормально. Добавление еще одной строки из @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 для ответов, получил идеальное решение, объединяющее все из них=)

выше было упомянуто, что метод mod_xsendfile не допускает символов, отличных от ASCII, в именах файлов.

по этой причине у меня есть патч, доступный для mod_xsendfile, который позволит отправлять любой файл, если имя закодировано url, и дополнительный заголовок:

X-SendFile-Encoding: url

отправлено также.

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

просто упомянуть FileResponse объект в Django 1.10

Edit: просто столкнулся с моим собственным ответом при поиске простого способа потоковой передачи файлов через Django, поэтому вот более полный пример (для будущего меня). Предполагается, что имя поля файла 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'),
...

попробуйте:https://pypi.python.org/pypi/django-sendfile/

"абстракция для разгрузки загрузки файлов на веб-сервер (например, Apache с mod_xsendfile) после того, как Django проверил разрешения и т. д."

вы должны использовать API sendfile, предоставленные популярными серверами, такими как apache или nginx в производстве. Много лет я использовал sendfile api этих серверов для защиты файлов. Затем создал простое приложение Django на основе промежуточного программного обеспечения для этой цели, подходящее как для разработки, так и для производства.Вы можете получить доступ к исходному коду здесь.
Обновление: в новой версии python провайдер использует django FileResponse если доступно, а также добавляет поддержку для многих реализациях сервера от lighthttp, caddy to hiawatha

использование

pip install django-fileprovider
  • добавить fileprovider приложение INSTALLED_APPS параметры
  • добавить fileprovider.middleware.FileProviderMiddleware до MIDDLEWARE_CLASSES настройки
  • set 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/;
 }

здесь nginx задает URL-адрес местоположения /files/ только доступ внутренний, если вы используете выше конфигурации вы можете установить X-файл как,

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

делая это с конфигурацией nginx, файл будет будьте защищены , а также вы можете управлять файлом из django views

Django рекомендует использовать другой сервер для обслуживания статических носителей (другой сервер, работающий на той же машине, в порядке.) Они рекомендуют использовать такие серверы как lighttp.

Это очень просто настроить. Однако. если ' какой-то файл.txt ' генерируется по запросу (контент динамический), тогда вы можете захотеть, чтобы django его обслуживал.

Django Docs-Статические Файлы

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 

еще один проект, чтобы посмотреть на: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)

Я столкнулся с той же проблемой более одного раза и поэтому реализован с помощью модуля xsendfile и auth view decorators django-filelibrary. Не стесняйтесь использовать его в качестве вдохновения для собственного решения.

https://github.com/danielsokolowski/django-filelibrary

предоставление защищенного доступа к статической папке html с помощью https://github.com/johnsensible/django-sendfile: https://gist.github.com/iutinvg/9907731