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)
PEP-484 type hints bindings for the Django web framework

mypy-django Type stubs to use the mypy static type-checker with your Django projects This project includes the PEP-484 compatible "type stubs" for Dja

Machinalis 223 Jun 17, 2022
Serve files with Django.

django-downloadview django-downloadview makes it easy to serve files with Django: you manage files with Django (permissions, filters, generation, ...)

Jazzband 328 Dec 07, 2022
Automated image processing for Django. Currently v4.0

ImageKit is a Django app for processing images. Need a thumbnail? A black-and-white version of a user-uploaded image? ImageKit will make them for you.

Matthew Dapena-Tretter 2.1k Dec 17, 2022
Radically simplified static file serving for Python web apps

WhiteNoise Radically simplified static file serving for Python web apps With a couple of lines of config WhiteNoise allows your web app to serve its o

Dave Evans 2.1k Dec 15, 2022
pdm-django: Django command shortcuts for PDM

pdm-django: Django command shortcuts for PDM A plugin that gives you command shortcuts for developing with PDM. pdm run python manage.py runserver -

Neutron Sync 2 Aug 11, 2022
DCM is a set of tools that helps you to keep your data in your Django Models consistent.

Django Consistency Model DCM is a set of tools that helps you to keep your data in your Django Models consistent. Motivation You have a lot of legacy

Occipital 59 Dec 21, 2022
Phoenix LiveView but for Django

Reactor, a LiveView library for Django Reactor enables you to do something similar to Phoenix framework LiveView using Django Channels. What's in the

Eddy Ernesto del Valle Pino 526 Jan 02, 2023
A quick way to add React components to your Django templates.

Django-React-Templatetags This django library allows you to add React (16+) components into your django templates. Features Include react components u

Fröjd Agency 408 Jan 08, 2023
A Django app to accept payments from various payment processors via Pluggable backends.

Django-Merchant Django-Merchant is a django application that enables you to use multiple payment processors from a single API. Gateways Following gate

Agiliq 997 Dec 24, 2022
A small Django app to easily broadcast an announcement across a website.

django-site-broadcasts The site broadcast application allows users to define short messages and announcements that should be displayed across a site.

Ben Lopatin 12 Jan 21, 2020
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
A BitField extension for Django Models

django-bitfield Provides a BitField like class (using a BigIntegerField) for your Django models. (If you're upgrading from a version before 1.2 the AP

DISQUS 361 Dec 22, 2022
:couple: Multi-user accounts for Django projects

django-organizations Summary Groups and multi-user account management Author Ben Lopatin (http://benlopatin.com / https://wellfire.co) Status Separate

Ben Lopatin 1.1k Jan 01, 2023
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
Agenda feita usando o django para adicionar eventos

Agenda de Eventos Projeto Agenda com Django Inicio O projeto foi iniciado no Django, usando o models.py foi adicionado os dados dos eventos e feita as

Bruno Fernandes 1 Apr 14, 2022
Use heroicons in your Django and Jinja templates.

heroicons Use heroicons in your Django and Jinja templates. Requirements Python 3.6 to 3.9 supported. Django 2.2 to 3.2 supported. Are your tests slow

Adam Johnson 52 Dec 14, 2022
💨 Fast, Async-ready, Openapi, type hints based framework for building APIs

Fast to learn, fast to code, fast to run Django Ninja - Fast Django REST Framework Django Ninja is a web framework for building APIs with Django and P

Vitaliy Kucheryaviy 3.8k Jan 01, 2023
System checks for your project's environment.

django-version-checks System checks for your project's environment. Requirements Python 3.6 to 3.9 supported. Django 2.2 to 3.2 supported. Are your te

Adam Johnson 33 Dec 22, 2022
Coltrane - A simple content site framework that harnesses the power of Django without the hassle.

coltrane A simple content site framework that harnesses the power of Django without the hassle. Features Can be a standalone static site or added to I

Adam Hill 58 Jan 02, 2023
Sampling profiler for Python programs

py-spy: Sampling profiler for Python programs py-spy is a sampling profiler for Python programs. It lets you visualize what your Python program is spe

Ben Frederickson 9.5k Jan 01, 2023