Django: How to let user download a file
Creating file download links with the help of a FileResponse.
Published: July 23, 2022 Sponsored App StoreIn this post, we will look at how to download a file with Django. Specifically how to let users download ImageField
or FileField
content.
While there are a few ways to accomplish this, using FileResponse
works really well. The most basic version is as simple as returning the FileResponse
from your view like this:
return FileResponse(model.image.open())
Here the image
property is ImageField
on a model.
This won’t download the file; rather the browser will open it in a new tab in most cases. A similar thing will also happen for other file types that browsers can preview.
To download it, we should specify the as_attachment
parameter:
return FileResponse(model.image.open(), as_attachment=True)
The downloaded file will keep whatever filename used during upload (assuming you aren’t generating those yourself during save), which may not be optimal.
Custom filename
Wanting to “override” the filename was the main reason for this blog post. It turns out, you can pass any filename you wish to FileResponse
, and that is the one browser will use when saving the file.
Passing the filename parameter will allow you to offer descriptive filenames and possibly avoid embarrassing moments.
return FileResponse(model.image.open(), as_attachment=True, filename="Export.png")
That works great, but what if only some images are PNGs and others are JPEGs?
In my testing, when the filename did not contain the extension, I could still download the files without issues, and macOS added the extension during saving. However, on Windows the files did not have an extension and looked like something went wrong. To be honest, I did not test this thoroughly, so perhaps it was due to different browsers.
The point is that we should specify file extension ourselves to avoid this problem.
We can use pathlib
to get the extension and add it to the generated filename:
extension = pathlib.Path(model.image.name).suffix
filename_with_extension = "{0}{1}".format(filename, extension)
return FileResponse(model.image.open(), as_attachment=True, filename=filename_with_extension)
Downloading generated image
If your web app provides images that it generated, and aren’t saved as file, you can still make them available to download without saving them first. But FileResponse
won’t help there.
response = HttpResponse(content_type='image/png')
image.save(response, 'png')
response['Content-Disposition'] = 'attachment; filename={0}'.format("Export.png")
return response
The header Content-Disposition
controls whether the file is downloaded (not previewed) and its filename. This is what FileResponse
does for us behind the scenes.
The above example assumes that image
is of type PIL.Image
.
Download FileField content
As mentioned previously, FileField
downloads work mostly the same. The difference is that we don't have to open the file.
return FileResponse(model.file, as_attachment=True)