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)
💨 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
Keep track of failed login attempts in Django-powered sites.

django-axes Axes is a Django plugin for keeping track of suspicious login attempts for your Django based website and implementing simple brute-force a

Jazzband 1.1k Dec 30, 2022
django-compat-lint

django_compat_lint -- check Django compatibility of your code Django's API stability policy is nice, but there are still things that change from one v

James Bennett 40 Sep 30, 2021
A reusable Django model field for storing ad-hoc JSON data

jsonfield jsonfield is a reusable model field that allows you to store validated JSON, automatically handling serialization to and from the database.

Ryan P Kilby 1.1k Jan 03, 2023
Neighbourhood - A python-django web app to help the residence of a given neighborhood know their surrounding better

Neighbourhood A python-django web app to help the residence of a given neighborh

Levy Omolo 4 Aug 25, 2022
Show how the redis works with Python (Django).

Redis Leaderboard Python (Django) Show how the redis works with Python (Django). Try it out deploying on Heroku (See notes: How to run on Google Cloud

Tom Xu 4 Nov 16, 2021
Django CRUD REST API Generator

Django CRUD REST API Generator This is a simple tool that generates a Django REST API with the given models. Specs: Authentication, DRF generic views,

Mehmet Alp Sümer 57 Nov 24, 2022
Django web apps for managing schedules.

skdue Description Skdue is a web application that makes your life easier by helping you manage your schedule. With the ability which allows you to cre

Patkamon_Awai 1 Jun 30, 2022
Django API creation with signed requests utilizing forms for validation.

django-formapi Create JSON API:s with HMAC authentication and Django form-validation. Version compatibility See Travis-CI page for actual test results

5 Monkeys 34 Apr 04, 2022
A Django app for working with BTCPayServer

btcpay-django A Django app for working with BTCPayServer Installation pip install btcpay-django Developers Release To cut a release, run bumpversion,

Crawford 3 Nov 20, 2022
A app for managing lessons with Django

Course Notes A app for managing lessons with Django Some Ideas

Motahhar.Mokfi 6 Jan 28, 2022
Easy thumbnails for Django

Easy Thumbnails A powerful, yet easy to implement thumbnailing application for Django 1.11+ Below is a quick summary of usage. For more comprehensive

Chris Beaven 1.3k Dec 30, 2022
Django Rest Framework + React application.

Django Rest Framework + React application.

2 Dec 19, 2022
Twitter Bootstrap for Django Form

Django bootstrap form Twitter Bootstrap for Django Form. A simple Django template tag to work with Bootstrap Installation Install django-bootstrap-for

tzangms 557 Oct 19, 2022
A simple E-commerce shop made with Django and Bulma

Interiorshop A Simple E-Commerce app made with Django Instructions Make sure you have python installed Step 1. Open a terminal Step 2. Paste the given

Aditya Priyadarshi 3 Sep 03, 2022
Updates redisearch instance with igdb data used for kimosabe

igdb-pdt Update RediSearch with IGDB games data in the following Format: { "game_slug": { "name": "game_name", "cover": "igdb_coverart_url",

6rotoms 0 Jul 30, 2021
Helps working with singletons - things like global settings that you want to edit from the admin site.

Django Solo +---------------------------+ | | | | | \ | Django Solo helps

Sylvain Toé 726 Jan 08, 2023
Example project demonstrating using Django’s test runner with Coverage.py

Example project demonstrating using Django’s test runner with Coverage.py Set up with: python -m venv --prompt . venv source venv/bin/activate python

Adam Johnson 5 Nov 29, 2021
Buckshot++ is a new algorithm that finds highly stable clusters efficiently.

Buckshot++: An Outlier-Resistant and Scalable Clustering Algorithm. (Inspired by the Buckshot Algorithm.) Here, we introduce a new algorithm, which we

John Jung 1 Jul 02, 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 09, 2022