Я хочу, чтобы пользователи на сайте могли загружать файлы, чьи пути скрыты, поэтому их нельзя напрямую загрузить.
Например, я хотел бы, чтобы URL-адрес был примерно таким: http://example.com/download/?f=somefile.txt
И на сервере я знаю, что все загружаемые файлы находятся в папке "/home/user/files/".
Есть ли способ заставить Django обслуживать этот файл для загрузки, а не пытаться найти URL-адрес и View для его отображения?
Для "лучшего из двух миров" вы можете объединить решение 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.
smart_str
не работает smart_str
образом, так как X-Sendfile модуля Apache не может декодировать строку, закодированную в smart_str. Так, например, файл «Örinää.mp3» не может быть предоставлен. И если не указывать smart_str, сам Django выдает ошибку кодирования ascii, потому что все заголовки перед отправкой кодируются в формат ascii. Единственный способ, который я знаю, чтобы обойти эту проблему - это сократить имена файлов X-sendfile до тех, которые состоят только из ascii.
"Загрузка" - это просто изменение заголовка 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 строк кода.
filepath = filepath.replace('..', '').replace('/', '')
Для очень простого , но не эффективного или масштабируемого решения вы можете просто использовать встроенное представление 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))
S.Lott имеет "хорошее" /простое решение, а у elo80ka есть "лучшее" /эффективное решение. Вот "лучшее" /среднее решение - не настройка сервера, но более эффективная для больших файлов, чем наивное исправление:
http://djangosnippets.org/snippets/365/
В принципе, Django по-прежнему обрабатывает файл, но не сразу загружает все это в память. Это позволяет вашему серверу (медленно) обслуживать большой файл, не увеличивая объем использования памяти.
Опять же, S.Lott X-SendFile все еще лучше для больших файлов. Но если вы не можете или не хотите этого беспокоиться, то это среднее решение поможет вам повысить эффективность без проблем.
django.core.servers.httpbase
, который имеет большой предупреждающий знак в верхней части кода « НЕ ИСПОЛЬЗОВАТЬ ДЛЯ ПРОИЗВОДСТВА ИСПОЛЬЗОВАНИЯ !!! », который находится в файле с момента его появления. впервые создан . В любом случае, функциональность FileWrapper
, на FileWrapper
опирается этот фрагмент, была удалена в django 1.9.
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 для ответов, получилось идеальное решение, объединяющее все из них =)
filename="%s"
в заголовке Content-Disposition, чтобы избежать проблем с пробелами в именах файлов. Ссылки: Имена файлов с пробелами усекаются при загрузке. Как кодировать параметр имени файла заголовка Content-Disposition в HTTP?
Было упомянуто выше, что метод mod_xsendfile не позволяет содержать символы не ASCII в именах файлов.
По этой причине у меня есть патч, доступный для mod_xsendfile, который позволит отправлять любой файл, если имя закодировано по URL и дополнительный заголовок:
X-SendFile-Encoding: url
Также отправляется.
Просто отметив FileResponse объект, доступный в Django 1.10
Edit: просто наткнулся на мой собственный ответ, ища простой способ потоковой передачи файлов через 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'),
...
Попробуйте: https://pypi.python.org/pypi/django-sendfile/
"Абстракция для загрузки файлов на веб-сервер (например, Apache с mod_xsendfile), когда Django проверяет разрешения и т.д.
Вы должны использовать 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
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
Django рекомендует использовать другой сервер для обслуживания статических носителей (другой сервер, работающий на одном компьютере, в порядке.) Они рекомендуют использовать такие серверы как lighttp.
Это очень просто настроить. Однако. если "somefile.txt" генерируется по запросу (содержимое является динамическим), вы можете захотеть, чтобы django обслуживал его.
Предоставление защищенного доступа к статической папке html с помощью https://github.com/johnsensible/django-sendfile: https://gist.github.com/iutinvg/9907731
Я столкнулся с одной и той же проблемой более чем один раз и так реализован с использованием модуля xsendfile и декодера вида просмотра django-filelibrary. Не стесняйтесь использовать его как вдохновение для своего собственного решения.
Еще один проект, чтобы посмотреть: 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)
django-private-files
...