Предоставление 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
Ответ 14
Предоставление защищенного доступа к статической папке html с помощью https://github.com/johnsensible/django-sendfile: https://gist.github.com/iutinvg/9907731
Ответ 15
Вы можете использовать этот простой модуль:
https://github.com/nishant-boro/django-rest-framework-download-expert
Этот модуль предоставляет простой способ обслуживания файлов для загрузки в django rest framework с использованием модуля Apache Xsendfile. Он также имеет дополнительную функцию загрузки загрузок только пользователям, принадлежащим к определенной группе.