Вопрос по django, python, download – Наличие в Django загружаемых файлов

220

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

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

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

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

Если вы хотите принять во внимание права пользователя, вам нужно предоставить файл через представление Django. Łukasz
Именно поэтому я и задаю этот вопрос. damon
Почему вы просто не используете Apache для этого? Apache обслуживает статический контент быстрее и проще, чем когда-либо мог Django. S.Lott
Я не использую Apache, потому что я не хочу, чтобы файлы были доступны без разрешений, основанных на Django. damon

Ваш Ответ

14   ответов
0

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

13

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

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

X-SendFile-Encoding: url

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

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

Патч теперь свернут в библиотеку corer.
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 
82

& Quot; скачать & quot; это просто изменение заголовка 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" Информация.

Ваша функция просмотра просто объединит базовый путь с & quot;f& Quot; значение, откройте файл, создайте и верните объект ответа. Должно быть меньше 12 строк кода.

Если вы используете таблицу для хранения информации о файле, включая информацию о том, какие пользователи должны иметь возможность загрузить ее, то все, что вам нужно отправить, - это первичный ключ, а не имя файла, и приложение решит, что делать.
very simple исправить эту проблему безопасности:filepath = filepath.replace('..', '').replace('/', '')
По сути, это правильный (простой) ответ, но одно предостережение - передача имени файла в качестве параметра означает, что пользователь потенциально может загрузитьany файл (т. е. что если вы передадите «f = / etc / passwd»?) Существует множество вещей, которые помогают предотвратить это (права пользователя и т. д.), но просто следует учитывать этот очевидный, но распространенный риск безопасности. По сути, это просто подмножество проверяющего ввода: если вы передаете имя файла в представление, проверьте имя файла в этом представлении!
7

Пытаться:https://pypi.python.org/pypi/django-sendfile/

& quot; Абстракция для разгрузки файлов, загружаемых на веб-сервер (например, Apache с mod_xsendfile) после проверки прав доступа Django и т. д. & quot;

Почему вы удалили ссылку?
В то время (1 год назад) на моем личном форке не было файла Apache, служащего запасным вариантом, к которому еще не был добавлен исходный репозиторий.
@ kiok46 Конфликт с политиками Github. Отредактировано, чтобы указать на канонический адрес.
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)
request.user.is_authenticated - это метод, а не атрибут. (not request.user.is_anonymous ()) - это то же самое, что и request.user.is_authenticated (), потому что is_authenticated является инверсией is_anonymous.
@explodes Еще хуже, этот код прямо из документовdjango-private-files...
179

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

EDIT:

mimetype is replaced by content_type for django 1.7

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

EDIT:  Заnginx проверятьэтот, оно используетX-Accel-Redirect вместоapache Заголовок X-Sendfile.

Чтобы быть более ясным: S.Lott имеет простой пример, просто обслуживая файлы прямо из django, никаких других настроек не требуется. elo80ka имеет более эффективный пример, где веб-сервер обрабатывает статические файлы, а django не обязан. Последний имеет лучшую производительность, но может потребовать больше настроек. У обоих есть свои места.
@Ciantic, см. Ответ btimby о том, что похоже на решение проблемы кодирования.
Работает ли это решение со следующей конфигурацией веб-сервера? Back-end: 2 или более отдельных сервера Apache + mod_wsgi (VPS), настроенных для репликации друг друга. Внешний интерфейс: 1 прокси-сервер nginx (VPS), использующий балансировку нагрузки в восходящем направлении и выполняющий циклический перебор.
mimetype заменяется на content_type для django 1.7
Если ваше имя файла или path_to_file содержит не-ascii символы, такие как & quot; & # xE4; & quot; или "& # xF6;",smart_str не работает должным образом, так как X-Sendfile модуля apache не может декодировать строку, закодированную в smart_str. Например, «& # xD6; rin & # xE4; & # xE4; .mp3" ». файл не может быть подан. И если не указать smart_str, сам Django выдает ошибку кодирования ascii, потому что всеheaders перед отправкой кодируются в формат ascii. Единственный способ обойти эту проблему, который мне известен, - это сократить имена файлов X-sendfile до тех, которые состоят только из ascii.
27

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

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

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

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

Этот фрагмент не очень хорош. Отрезанный опирается наdjango.core.servers.httpbase закрытый недокументированный закрытый модуль с большим предупреждающим знаком в верхней части кода & quot;DON'T USE FOR PRODUCTION USE!!!& quot ;, который был в файлеsince it was first created, В любом случае,FileWrapper функциональность этого фрагмента была удалена в django 1.9.
27

Для очень простогоbut not efficient or scalable Решение, вы можете просто использовать встроенный в Djangoserve Посмотреть. Это отлично подходит для быстрых прототипов или разовой работы, но, как уже упоминалось в этом вопросе, вы должны использовать что-то вроде 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))
Я делаю отдельный проект django, предназначенный для работы вроде настольного клиента, и это сработало отлично. Спасибо!
О каких недостатках производительности мы говорим здесь? Загружаются ли файлы в оперативную память или что-то в этом роде, если они обслуживаются через django? Почему django не может работать с той же эффективностью, что и nginx?
Также очень полезно для предоставления запасного варианта для тестирования в Windows.
почему это не эффективно?
@zinking, потому что файлы, как правило, должны обслуживаться через что-то вроде apache, а не через процесс django
12

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

Изменить: Просто столкнулся с моим собственным ответом при поиске простого способа для потоковой передачи файлов через Django, так что вот более полный пример (для будущего меня). Предполагается, что имя FileFieldimported_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'),
...
Большое спасибо! Это самое простое решение для загрузки бинарных файлов, и оно работает.
15

Пробовал решение @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 для определенных пользователей или при определенных обстоятельствах.
Надеюсь, поможет.

Thanks to @elo80ka, @S.Lott and @Rocketmonkeys for the answers, got the perfect solution combining all of them =)

Добавьте двойные кавычки вокруг имени файлаfilename="%s" в заголовке Content-Disposition, чтобы избежать проблем с пробелами в именах файлов. Рекомендации:Filenames with spaces are truncated upon download, How to encode the filename parameter of Content-Disposition header in HTTP?
Спасибо, это было именно то, что я искал!
Ваши решения работают на меня. Но у меня был "неверный начальный байт ..." ошибка для моего файла. Решил это сFileWrapper(open(path.abspath(file_name), 'rb'))
FileWrapper был удален с Django 1.9
Можно использоватьfrom wsgiref.util import FileWrapper
0

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

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

6

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

Usage

pip install django-fileprovider
add fileprovider app to INSTALLED_APPS settings, add fileprovider.middleware.FileProviderMiddleware to MIDDLEWARE_CLASSES settings set FILEPROVIDER_NAME settings to nginx or apache in production, by default it is python for development purpose.

в вашем представлении классов или функций установите заголовок ответа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 configuration

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

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

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

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

Делая это с конфигурацией nginx, файл будет защищен & amp; также вы можете контролировать файл из Djangoviews

2

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

Это очень просто настроить. Тем не мение. if 'somefile.txt & apos; генерируется по запросу (контент динамический), тогда вы можете захотеть, чтобы django его обслуживал.

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

Похожие вопросы