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)
Owner
Jazzband
We are all part of this
Jazzband
Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards and optional settings files.

Organize Django settings into multiple files and directories. Easily override and modify settings. Use wildcards in settings file paths and mark setti

Nikita Sobolev 940 Jan 3, 2023
django Filer is a file management application for django that makes handling of files and images a breeze.

django Filer is a file management application for django that makes handling of files and images a breeze.

django CMS Association 1.6k Jan 6, 2023
Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project

Django URL Shortener Django URL Shortener is a Django app to to include URL Shortening feature in your Django Project Install this package to your Dja

Rishav Sinha 4 Nov 18, 2021
A Django application that provides country choices for use with forms, flag icons static files, and a country field for models.

Django Countries A Django application that provides country choices for use with forms, flag icons static files, and a country field for models. Insta

Chris Beaven 1.2k Jan 7, 2023
A Django app for managing robots.txt files following the robots exclusion protocol

Django Robots This is a basic Django application to manage robots.txt files following the robots exclusion protocol, complementing the Django Sitemap

Jazzband 406 Dec 26, 2022
A Django application that provides country choices for use with forms, flag icons static files, and a country field for models.

Django Countries A Django application that provides country choices for use with forms, flag icons static files, and a country field for models. Insta

Chris Beaven 1.2k Dec 31, 2022
Meta package to combine turbo-django and stimulus-django

Hotwire + Django This repository aims to help you integrate Hotwire with Django ?? Inspiration might be taken from @hotwired/hotwire-rails. We are sti

Hotwire for Django 31 Aug 9, 2022
django-reversion is an extension to the Django web framework that provides version control for model instances.

django-reversion django-reversion is an extension to the Django web framework that provides version control for model instances. Requirements Python 3

Dave Hall 2.8k Jan 2, 2023
Django-environ allows you to utilize 12factor inspired environment variables to configure your Django application.

Django-environ django-environ allows you to use Twelve-factor methodology to configure your Django application with environment variables. import envi

Daniele Faraglia 2.7k Jan 7, 2023
Rosetta is a Django application that eases the translation process of your Django projects

Rosetta Rosetta is a Django application that facilitates the translation process of your Django projects. Because it doesn't export any models, Rosett

Marco Bonetti 909 Dec 26, 2022
Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly.

Cookiecutter Django Powered by Cookiecutter, Cookiecutter Django is a framework for jumpstarting production-ready Django projects quickly. Documentati

Daniel Feldroy 10k Dec 31, 2022
Django project starter on steroids: quickly create a Django app AND generate source code for data models + REST/GraphQL APIs (the generated code is auto-linted and has 100% test coverage).

Create Django App ?? We're a Django project starter on steroids! One-line command to create a Django app with all the dependencies auto-installed AND

imagine.ai 68 Oct 19, 2022
django-quill-editor makes Quill.js easy to use on Django Forms and admin sites

django-quill-editor django-quill-editor makes Quill.js easy to use on Django Forms and admin sites No configuration required for static files! The ent

lhy 139 Dec 5, 2022
A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, celery and redis.

Django Channels Websocket Chatbot A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, c

Yunbo Shi 8 Oct 28, 2022
A handy tool for generating Django-based backend projects without coding. On the other hand, it is a code generator of the Django framework.

Django Sage Painless The django-sage-painless is a valuable package based on Django Web Framework & Django Rest Framework for high-level and rapid web

sageteam 51 Sep 15, 2022
A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a unique id.

Django-URL-Shortener A beginner django project and also my first Django project which involves shortening of a longer URL into a short one using a uni

Rohini Rao 3 Aug 8, 2021
Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot. A fully Django starter project.

Dockerizing Django with Postgres, Gunicorn, Nginx and Certbot ?? Features A Django stater project with fully basic requirements for a production-ready

null 8 Jun 27, 2022
pytest-django allows you to test your Django project/applications with the pytest testing tool.

pytest-django allows you to test your Django project/applications with the pytest testing tool.

pytest-dev 1.1k Dec 14, 2022
APIs for a Chat app. Written with Django Rest framework and Django channels.

ChatAPI APIs for a Chat app. Written with Django Rest framework and Django channels. The documentation for the http end points can be found here This

Victor Aderibigbe 18 Sep 9, 2022