Serve files with Django.

Overview

django-downloadview

Jazzband GitHub Actions Coverage

django-downloadview makes it easy to serve files with Django:

  • you manage files with Django (permissions, filters, generation, ...);
  • files are stored somewhere or generated somehow (local filesystem, remote storage, memory...);
  • django-downloadview helps you stream the files with very little code;
  • django-downloadview helps you improve performances with reverse proxies, via mechanisms such as Nginx's X-Accel or Apache's X-Sendfile.

Example

Let's serve a file stored in a file field of some model:

from django.conf.urls import url, url_patterns
from django_downloadview import ObjectDownloadView
from demoproject.download.models import Document  # A model with a FileField

# ObjectDownloadView inherits from django.views.generic.BaseDetailView.
download = ObjectDownloadView.as_view(model=Document, file_field='file')

url_patterns = ('',
    url('^download/(?P<slug>[A-Za-z0-9_-]+)/$', download, name='download'),
)

Resources

Comments
  • PathDownloadView python3 compatibility

    PathDownloadView python3 compatibility

    As a relatively new django/python programmer, I am attempting to use downloadview in my project. I set up a test case from the demo, which works in python2.7, but not in python3.3. The tests are with django 1.5.4 (had similar results with 1.6 and 1.7). The results seem to suggest a problem with django, rather than downloadview, but I wanted to check here first.

    In attempting to use download_hello_world=views.PathDownloadView.as_view(path=hello_world_path)

    I got the following error


    Traceback (most recent call last): File "/usr/lib64/python3.3/wsgiref/handlers.py", line 138, in run self.finish_response() File "/usr/lib64/python3.3/wsgiref/handlers.py", line 178, in finish_response for data in self.result: File "/home/jeremiah/.virtualenvs/auto_p3_d15/lib/python3.3/site-packages/django/http/response.py", line 223, in __next__ return self.make_bytes(next(self._iterator)) File "/home/jeremiah/.virtualenvs/auto_p3_d15/lib/python3.3/site-packages/django/core/files/base.py", line 97, in __iter__ chunk_buffer = BytesIO(chunk) TypeError: 'str' does not support the buffer interface [23/Oct/2013 22:34:13] "GET /download/hello-world.txt HTTP/1.1" 500 59


    So I added a number of print() statements to downloadview to determine the point of failure, but it seems the problem is not in downloadview, but in django. So in dango/core/files/base.py::Files::__iter__() I converted chunk_buffer = BytesIO(chunk) to chunk_buffer = BytesIO(str.encode(chunk))

    And now PathDownloadView works with python 3 and 2.

    Before filing a bug in django, I wanted to check if this would be the proper fix. It seems unlikely I've discovered such a bug in django.

    Thanks.

    bug discussion 
    opened by jeremiahsavage 12
  • Update compatibility for Django 4.0

    Update compatibility for Django 4.0

    The only documented compatibility change is removing use of force_text (which was deprecated in Django 3.0) in favor of force_str (which has existed since before Django 1.11). There are also some changes in middleware because Django's MiddlewareMixin started requiring a get_response argument to the constructor.

    Fixes #187.

    opened by tari 10
  • Implement signatures for file download URLs

    Implement signatures for file download URLs

    Hi,

    tl; dr: Would this project benefit from the ability to sign download URLs (cryptographically and with expiration)?

    I thought I would open a discussion on adding download URL signatures.

    I recently implemented cryptographic authorization on top of URLs that were generated directly by the storage backend, much like with S3.

    These were served with nginx by using X-Accel and a custom view that generated serve requests to the proxy server, offloading the file serving from Django.

    The idea is fairly simple and I think many people could benefit from it. Implementation just requires

    • a Storage class mixin for specializing URL generation to add signatures in query parameters, and;
    • a decorator that validates file download URLs for the download views.

    The best thing is that download views will work with or without the signature.

    Following a naive example of the idea of the implementation. Please bear in mind that these examples are untested and would, of course, need to be further adapted for django_downloadview.

    # django_downloadview/storage.py
    
    from django.conf import settings
    from django.core.files.storage import FileSystemStorage
    from django.core.signing import BadSignature, SignatureExpired, TimestampSigner
    
    
    class SignedURLMixin(Storage):
        """ Mixin for generating signed file URLs with storage backends. Adds X-Signature query parameter to the normal URLs generated by the storage backend."""
    
        def url(self, name):
            signer = TimestampSigner()
            expiration = getattr(settings, "DOWNLOADVIEW_URL_EXPIRATION", None)
    
            path = super(SignedURLMixin, self).url(name)
            signature = signer.sign(path)
            return '{}?X-Signature={}'.format(path, signature)
    
    
    class SignedFileSystemStorage(SignedURLMixin, FileSystemStorage):
        pass
    
    # django_downloadview/decorators.py
    
    from functools import wraps
    
    from django.core.exceptions import PermissionDenied
    
    def signature_required(function):
        """ Decorator that checks for X-Signature query parameter to authorize specific user access. """
    
        @wraps
        def decorator(request, *args, **kwargs):
            signer = TimestampSigner()
            signature = request.GET.get("X-Signature")
            expiration = getattr(settings, "DOWNLOADVIEW_URL_EXPIRATION", None)
    
            try:
                signature_path = signer.unsign(signature, max_age=expiration)
            except SignatureExpired as e:
                raise PermissionDenied("Signature expired") from e
            except BadSignature as e:
                raise PermissionDenied("Signature invalid") from e
            except Exception as e:
                raise PermissionDenied("Signature error") from e
    
            if request.path != signature_path:
                raise PermissionDenied("Signature mismatch")
    
            return function(request, *args, **kwargs)
    
        return decorator
    

    Then the usage can simply be:

    # demoproject/urls.py
    
    # Django is set up with
    # DEFAULT_FILE_STORAGE='example.storage.SignedFileSystemStorage'
    
    from django.conf.urls import url, url_patterns
    from django_downloadview import ObjectDownloadView
    from django_downloadview.decorators import signature_required
    
    from demoproject.download.models import Document  # A model with a FileField
    
    # ObjectDownloadView inherits from django.views.generic.BaseDetailView.
    download = ObjectDownloadView.as_view(model=Document, file_field='file')
    
    url_patterns = ('',
        url('^download/(?P<slug>[A-Za-z0-9_-]+)/$', signature_required(download), name='download'),
    )
    
    
    {# demoproject/download/template.html #}
    {# URLs in templates are generated with the storage class URL implementation #}
    
    <a href="{{ object.file.url  }}">Click here to download.</a>
    

    The S3 Boto storage backend uses a similar approach and makes it possible to generate URLs in user templates and then authorize S3 access with those URLs. This vanilla Django approach makes it very easy to emulate that behaviour.

    Additional hardening can then be achieved with:

    • Adding random salts to signing, and expiration times to the TimestampSigner
    • Only ever using signed download links generated with the storage backend using {{ file.url }}

    This approach only lacks in that it introduces non-cacheable URLs that require slight computation to decrypt.

    Inspiration was received from Grok. You can find more information on generic URL signatures in his weblog:

    • http://grokcode.com/819/one-click-unsubscribes-for-django-apps/

    If signatures are appended to URLs with existing query parameters, a more sophisticated solution has to be used. For example:

    • https://stackoverflow.com/questions/2506379/add-params-to-given-url-in-python
    opened by aleksihakli 7
  • Use VirtualDownloadView to generate downloadable PDF file

    Use VirtualDownloadView to generate downloadable PDF file

    Basically I want to generate downloadable pdf file based on dynamic html content passed by ajax from client side. The html is in the request.GET, how can I access this value in the StringIODownloadView?

    From the official doc:

    from StringIO import StringIO
    
    from django_downloadview import VirtualDownloadView
    from django_downloadview import VirtualFile
    
    
    class StringIODownloadView(VirtualDownloadView):
        def get_file(self):
            """Return wrapper on ``StringIO`` object."""
            file_obj = StringIO(u"Hello world!\n")
            return VirtualFile(file_obj, name='hello-world.txt')
    

    I am not sure how to use the VirtualDownloadView, since there is not a demo for this. Any help is much appreciated.

    feature 
    opened by sfdye 7
  • How to use HTTPDownloadView with generic.View

    How to use HTTPDownloadView with generic.View

    I want to serve download files based on the request from an external server.I cannot fix the download url as there are many files.How to use the HTTPDownloadView with it.

    opened by apoorvaeternity 6
  • Implement Jazzband guidelines for django-downloadview

    Implement Jazzband guidelines for django-downloadview

    This issue tracks the implementation of the Jazzband guidelines for the project django-downloadview

    It was initiated by @benoitbryon who was automatically assigned in addition to the Jazzband roadies.

    See the TODO list below for the generally required tasks, but feel free to update it in case the project requires it.

    Feel free to ping a Jazzband roadie if you have any question.

    TODOs

    • [x] Fix all links in the docs (and README file etc) from old to new repo
    • [x] Add the Jazzband badge to the README file
    • [x] Add the Jazzband contributing guideline to the CONTRIBUTING.md or CONTRIBUTING.rst file
    • [x] Check if continuous testing works (e.g. Travis CI, CircleCI, AppVeyor, etc)
    • [x] Check if test coverage services work (e.g. Coveralls, Codecov, etc)
    • [x] Add jazzband account to PyPI project as maintainer role (e.g. URL: https://pypi.org/manage/project/django-downloadview/collaboration/)
    • [x] Add jazzband-bot as maintainer to the Read the Docs project (e.g. URL: https://readthedocs.org/dashboard/django-downloadview/users/)
    • [x] Add incoming GitHub webhook integration to Read the Docs project (e.g. URL: https://readthedocs.org/dashboard/django-downloadview/integrations/)
    • [x] Fix project URL in GitHub project description
    • [x] Review project if other services are used and port them to Jazzband
    • [x] Decide who is project lead for the project (if at all)
    • [x] Set up CI for Jazzband project releases if needed and open ticket if yes → Refs jazzband-roadies/help#201

    Project details

    Description Serve files with Django.
    Homepage https://django-downloadview.readthedocs.io
    Stargazers 190
    Open issues 29
    Forks 39
    Default branch master
    Is a fork False
    Has Wiki False
    Has Pages False
    opened by jazzband-bot 5
  • Avoid calling PathDownloadView.get_path() twice inside get_file()

    Avoid calling PathDownloadView.get_path() twice inside get_file()

    Overridden PathDownloadView.get_path() may contain database lookups and logging which should not be called twice if not necessary, as it was in my case. Because the acquired filename does not change inside get_file(), I replaced the duplicate call.

    bug 
    opened by rleonhardt 5
  • TypeError: int() argument must be a string or a number, not 'datetime.datetime'

    TypeError: int() argument must be a string or a number, not 'datetime.datetime'

    Hello!

    Some times i catched this exception when downloaded file. After some digging i found why.

    This happens when request has If-Modified-Since header.

    Exception raised here: https://github.com/benoitbryon/django-downloadview/blob/master/django_downloadview/views/base.py#L119

    def was_modified_since(self, file_instance, since):
            """Return True if ``file_instance`` was modified after ``since``.
            Uses file wrapper's ``was_modified_since`` if available, with value of
            ``since`` as positional argument.
            Else, fallbacks to default implementation, which uses
            :py:func:`django.views.static.was_modified_since`.
            Django's ``was_modified_since`` function needs a datetime and a size.
            It is passed ``modified_time`` and ``size`` attributes from file
            wrapper. If file wrapper does not support these attributes
            (``AttributeError`` or ``NotImplementedError`` is raised), then
            the file is considered as modified and ``True`` is returned.
            """
            try:
                return file_instance.was_modified_since(since)
            except (AttributeError, NotImplementedError):
                try:
                    modification_time = file_instance.modified_time
                    size = file_instance.size
                except (AttributeError, NotImplementedError):
                    return True
                else:
                    return was_modified_since(since, modification_time, size)
    

    because modification_time it is realy datetime object for default storage (FileSystemStorage)

    https://github.com/django/django/blob/master/django/core/files/storage.py#L324

        def modified_time(self, name):
            return datetime.fromtimestamp(os.path.getmtime(self.path(name)))
    

    I can to contribute if needed.

    bug 
    opened by zerc 4
  • DownloadView and DownloadResponse use some file wrapper

    DownloadView and DownloadResponse use some file wrapper

    In download views, we want to instanciate a download response. We currently compute file attributes (size, name, ...) and pass it to the response constructor. The more attributes (see #21), the more the response class gets complicated. Moreover we would like these attributes (see #22) to be lazy. We could implement some methods that support storages (and thus, FileField and ImageField). But then, what if someone wants to change the behaviour? He would have to override the response class.

    The response class should have some "file" (or whatever the name) attribute, and handle it via some API. Could sound like "the response's file attribute is an object with url, filename, basename, size... attributes". Then it would make it possible to have various implementations for this "file wrapper". One would use Django storages.

    Could look like django.db.models.fields.files.FieldFile, but not tied to models.

    Could help #5, #21 and #22.

    List of attributes for the file wrapper:

    • name: absolute filename in filesystem
    • url: URL where file contents live
    • size: in bytes
    • mime_type
    • encoding
    • charset
    • modification_time
    • content: iterator over file contents

    Are these attributes needed?

    • media_root: typically storage's location
    • relative_filename: filename relative to media_root
    • is_virtual: True if the file isn't on some disk, i.e. Django/Python is to compute file contents. Should be True if filename (and maybe URL too) is empty. Could also be "is_persistent", i.e. will the file live after Django processed it.
    refactoring 
    opened by benoitbryon 4
  • Replace mention of deprecated NGINX_DOWNLOAD_MIDDLEWARE_SOURCE_DIR

    Replace mention of deprecated NGINX_DOWNLOAD_MIDDLEWARE_SOURCE_DIR

    NGINX_DOWNLOAD_MIDDLEWARE_MEDIA_ROOT is deprecated in favor of NGINX_DOWNLOAD_MIDDLEWARE_SOURCE_DIR

    https://github.com/jazzband/django-downloadview/blob/563b2a4f7b354cb930aa7067e6e1341dbb34f5e7/django_downloadview/nginx/middlewares.py#L120-L121

    opened by johnthagen 3
  • Proposal to move django-downloadview to JazzBand.

    Proposal to move django-downloadview to JazzBand.

    • [x] Add Jazzband a badge in the readme
    • [x] Make sure there is a CONTRIBUTING documentation
    • [x] Add the contributing JazzBand header

    Full guidelines: https://jazzband.co/about/guidelines

    • [ ] Open an issue to ask roadies to configure a travis rule for deployment to pypi.
    opened by Natim 3
  • How to use django-downloadview for ensuring only authenticated users can access media files?

    How to use django-downloadview for ensuring only authenticated users can access media files?

    I have similar question as #129

    the only difference is that I'm using nginx. I'm not so interested about performance, but only that I want to ensure only authenticated users can access media files.

    That's all.

    The documentation I have tried reading e,g, https://django-downloadview.readthedocs.io/en/latest/optimizations/nginx.html?highlight=media#setup-xaccelredirect-middlewares but still none the wiser.

    They seem to emphasize for performance. Not authentication or privacy.

    Can help?

    opened by simkimsia 0
  • Upgrade GitHub Actions

    Upgrade GitHub Actions

    https://github.com/actions/checkout/releases https://github.com/actions/setup-python/releases https://github.com/actions/cache/releases https://github.com/codecov/codecov-action/releases

    opened by cclauss 0
  • Signal/event once file has been downloaded?

    Signal/event once file has been downloaded?

    When applications want to indicate to users whether they have seen/downloaded a document or not, this would probably happen with the help of a boolean has_been_downloaded model field on the corresponding model.

    To actually turn this field true once a document has been downloaded, it would be really helpful if the package could send a signal, offer a hook, that gets triggered, when a document was shipped successfully.

    I guess this could be implemented with some custom code in a custom view that wraps one of the package views.

    DRF example:

        @action(detail=True)
        def download(self, request, pk):
            document = self.get_object()
    
            # When the owner downloads the document, we want to update the
            # has_been_downloaded field correspondingly
            if document.has_been_downloaded is False:
                document.has_been_downloaded = True
                document.save()
    
            return ObjectDownloadView.as_view(
                model=Document,
            )(request, pk=pk)
    

    But this would always happen before the actual download code runs and therefore, when the download somehow fails, data would wrongly be changed in the model.

    opened by devidw 0
  • Very slow download speed from `ObjectDownloadView`

    Very slow download speed from `ObjectDownloadView`

    I have an ObjectDownloadView that is serving very large files (200MB - 2GB). I've observed that the download speed is very slow, even when pulling downloads over localhost where no actual network is involved at all. I must authenticate the file downloads (not shown the example below), which is why I must use django-downloadview rather than serving them statically.

    I cannot use NGINX acceleration due to:

    • #177

    My endpoint looks something like:

    class MyModelObjectDownloadView(ObjectDownloadView):
        model_class = MyModel
        file_field = "model"
        basename_field = "filename"
    

    The Model:

    class MyModel(models.Model):
        MODEL_UPLOAD_TO_DIR = "models"
        model = models.FileField(upload_to=MODEL_UPLOAD_TO_DIR)
        filename = models.TextField()
    

    URLs:

    urlpatterns = [
        ...
        path(
            f"media/{MyModel.MODEL_UPLOAD_TO_DIR}/<int:pk>/",
            MyModelObjectDownloadView.as_view(),
            name=SurfaceModel.IMAGE_UPLOAD_TO_DIR,
        ),
    ]
    

    I've tested downloading the file using a variety of clients over localhost (also running locally on mac and also within a Linux Docker container) and they all show a similar result:

    • httpx
    • Chrome
    • curl
    $ curl http://localhost:8000/media/models/1/ --output out.bin
      % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                     Dload  Upload   Total   Spent    Left  Speed
    100  118M  100  118M    0     0  5398k      0  0:00:22  0:00:22 --:--:-- 5663k
    

    This corresponds to about 40Mbps, which seemed very slow for a localhost pull. I also see the python executable running at about 100% CPU, as if it's CPU rather than I/O bound?

    Is there something about how django-downloadview streams or chunks the file that contributes to why this is so slow?

    Are there any configuration settings to speed up serving files natively from django-downloadview?

    opened by johnthagen 2
  • Use Django's built-in FileResponse to address security issue

    Use Django's built-in FileResponse to address security issue

    Django recently released a patch that addresses CVE-2022-36359

    I am concerned that since this library does not use Django's FileResponse, it may be vulnerable to a similar type of attack and will not benefit from Django's patch.

    After copying test case from the django patch and running it against DownloadView, I noticed that it does not pass so it is possible that the DownloadView is not as secure since it does not escape file names.

    opened by mick88 1
  • Add Async Support

    Add Async Support

    Currently, the DownloadView operates as a sync view. It's very possible (and much more efficient) to do everything async.

    File reading will have to be done via aiofile in order to not break the ASGI event queue though. After this change, Django versions will need to be limited to 3.1+

    feature 
    opened by Archmonger 2
Releases(2.3.0)
Django Girls Tutorial Workshop

Django Girls Tutorial Workshop A log of activities during the workshop. this is an H2 git remote add origin https://github.com/ahuimanu/django_girls_t

Jeffry Babb 1 Oct 27, 2021
Resolve form field arguments dynamically when a form is instantiated

django-forms-dynamic Resolve form field arguments dynamically when a form is instantiated, not when it's declared. Tested against Django 2.2, 3.2 and

DabApps 108 Jan 03, 2023
Django model mixins and utilities.

django-model-utils Django model mixins and utilities. django-model-utils supports Django 2.2+. This app is available on PyPI. Getting Help Documentati

Jazzband 2.4k Jan 04, 2023
Python port of Google's libphonenumber

phonenumbers Python Library This is a Python port of Google's libphonenumber library It supports Python 2.5-2.7 and Python 3.x (in the same codebase,

David Drysdale 3.1k Jan 08, 2023
Simple application TodoList django with ReactJS

Django & React Django We basically follow the Django REST framework quickstart guide here. Create backend folder with a virtual Python environment: mk

Flavien HUGS 2 Aug 07, 2022
Django-Text-to-HTML-converter - The simple Text to HTML Converter using Django framework

Django-Text-to-HTML-converter This is the simple Text to HTML Converter using Dj

Nikit Singh Kanyal 6 Oct 09, 2022
Wrapping Raml around Django rest-api's

Ramlwrap is a toolkit for Django which allows a combination of rapid server prototyping as well as enforcement of API definition from the RAML api. R

Jmons 8 Dec 27, 2021
TinyMCE integration for Django

django-tinymce django-tinymce is a Django application that contains a widget to render a form field as a TinyMCE editor. Quickstart Install django-tin

Jazzband 1.1k Dec 26, 2022
A reusable Django app that configures your project for deployment

django-simple-deploy This app gives you a management command that configures your project for an initial deployment. It targets Heroku at the moment,

Eric Matthes 205 Dec 26, 2022
Dashboad Full Stack utilizando o Django.

Dashboard FullStack completa Projeto finalizado | Informações Cadastro de cliente Menu interatico mostrando quantidade de pessoas bloqueadas, liberada

Lucas Silva 1 Dec 15, 2021
Intellicards-backend - A Django project bootstrapped with django-admin startproject mysite

Intellicards-backend - A Django project bootstrapped with django-admin startproject mysite

Fabrizio Torrico 2 Jan 13, 2022
A middleware to log the requests and responses using loguru.

Django Loguru The extension was based on another one and added some extra flavours. One of the biggest problems with the apps is the logging and that

Tiago Silva 9 Oct 11, 2022
Automatically upgrade your Django projects.

django-upgrade Automatically upgrade your Django projects. Installation Use pip: python -m pip install django-upgrade Python 3.8 to 3.10 supported. Or

Adam Johnson 525 Dec 29, 2022
Advanced school management system written in Django :)

Advanced school management system written in Django :) ⚙️ Config the project First you should make venv for this project. So in the main root of proje

AminAli Mazarian 72 Dec 05, 2022
AUES Student Management System Developed for laboratory works №9 Purpose using Python (Django).

AUES Student Management System (L M S ) AUES Student Management System Developed for laboratory works №9 Purpose using Python (Django). I've created t

ANAS NABIL 2 Dec 06, 2021
A debug/profiling overlay for Django

Django Debug Toolbar The Django Debug Toolbar is a configurable set of panels that display various debug information about the current request/respons

David Cramer 228 Oct 17, 2022
Add a help desk or knowledge base to your Django project with only a few lines of boilerplate code.

This project is no longer maintained. If you are interested in taking over the project, email Zapier 487 Dec 06, 2022

TinyApp - A Python (Django) Full Stack Application for shortening URLs

TinyApp A Python (Django) Full Stack Application for shortening URLs. How to sta

Li Deng 1 Jan 23, 2022
A music recommendation REST API which makes a machine learning algorithm work with the Django REST Framework

music-recommender-rest-api A music recommendation REST API which makes a machine learning algorithm work with the Django REST Framework How it works T

The Reaper 1 Sep 28, 2021
An automatic django's update checker and MS teams notifier

Django Update Checker This is small script for checking any new updates/bugfixes/security fixes released in django News & Events and sending correspon

prinzpiuz 4 Sep 26, 2022