Предоставление Django загружаемых файлов

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

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

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

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

Ответ 1

Для "лучшего из двух миров" вы можете объединить решение S.Lott с xsendfile module: django создает путь к файлу ( или самого файла), но фактическая обработка файлов обрабатывается Apache/Lighttpd. После того, как вы установили файл mod_xsend, интеграция с вашим видом займет несколько строк кода:

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 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.

Ответ 2

"Загрузка" - это просто изменение заголовка 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 строк кода.

Ответ 3

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

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

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

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

Ответ 4

Для очень простого , но не эффективного или масштабируемого решения вы можете просто использовать встроенное представление 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))

Ответ 5

Tried @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 nor/public_html) и подвергать их через django определенным пользователям или при определенных обстоятельствах.
Надеюсь, поможет.

Благодаря @elo80ka, @S.Lott и @Rocketmonkeys для ответов, получилось идеальное решение, объединяющее все из них =)

Ответ 6

Просто упомяну объект 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'),
...

Ответ 7

Было упомянуто выше, что метод mod_xsendfile не позволяет содержать символы не ASCII в именах файлов.

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

X-SendFile-Encoding: url

Также отправляется.

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

Ответ 8

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

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

Ответ 9

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

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

pip install django-fileprovider
  • добавить fileprovider приложение в настройки INSTALLED_APPS,
  • добавить fileprovider.middleware.FileProviderMiddleware в MIDDLEWARE_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 impemented таким образом, чтобы ваш код нуждался только в минимальной модификации.

Конфигурация Nginx

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

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

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

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

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

Ответ 10

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

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

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

Ответ 11

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 

Ответ 12

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

Ответ 13

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

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

Ответ 15

Вы можете использовать этот простой модуль:

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

Этот модуль предоставляет простой способ обслуживания файлов для загрузки в django rest framework с использованием модуля Apache Xsendfile. Он также имеет дополнительную функцию загрузки загрузок только пользователям, принадлежащим к определенной группе.