PathDownloadView

PathDownloadView serves file given a path on local filesystem.

Use this view whenever you just have a path, outside storage or model.

Warning

Take care of path validation, especially if you compute paths from user input: an attacker may be able to download files from arbitrary locations. In most cases, you should consider managing files in storages, because they implement default security mechanisms.

Simple example

Setup a view to stream files given path:

import os

from django_downloadview import PathDownloadView

# Let's initialize some fixtures.
app_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(app_dir)
fixtures_dir = os.path.join(project_dir, "fixtures")
#: Path to a text file that says 'Hello world!'.
hello_world_path = os.path.join(fixtures_dir, "hello-world.txt")

#: Serve ``fixtures/hello-world.txt`` file.
static_path = PathDownloadView.as_view(path=hello_world_path)

Base options

PathDownloadView inherits from DownloadMixin, which has various options such as basename or attachment.

Computing path dynamically

Override the PathDownloadView.get_path() method to adapt path resolution to your needs:

import os

from django_downloadview import PathDownloadView

# Let's initialize some fixtures.
app_dir = os.path.dirname(os.path.abspath(__file__))
project_dir = os.path.dirname(app_dir)
fixtures_dir = os.path.join(project_dir, "fixtures")
#: Path to a text file that says 'Hello world!'.

class DynamicPathDownloadView(PathDownloadView):
    """Serve file in ``settings.MEDIA_ROOT``.

    .. warning::

       Make sure to prevent "../" in path via URL patterns.

    .. note::

       This particular setup would be easier to perform with
       :class:`StorageDownloadView`

    """

    def get_path(self):
        """Return path inside fixtures directory."""
        # Get path from URL resolvers or as_view kwarg.
        relative_path = super(DynamicPathDownloadView, self).get_path()
        # Make it absolute.
        absolute_path = os.path.join(fixtures_dir, relative_path)
        return absolute_path


dynamic_path = DynamicPathDownloadView.as_view()

The view accepts a path argument you can setup either in as_view or via URLconfs:

from django.urls import path, re_path

from demoproject.path import views

app_name = "path"
urlpatterns = [
    path("static-path/", views.static_path, name="static_path"),
    re_path(
        r"^dynamic-path/(?P<path>[a-zA-Z0-9_-]+\.[a-zA-Z0-9]{1,4})$",
        views.dynamic_path,
        name="dynamic_path",
    ),
]

API reference