Is there a way to make a button download a file?

That’s a good question! The info is all in the docs, but we haven’t brought it all together into a tutorial yet. I have added “make a tutorial about file downloads” to our TODO list, but for now here’s a quick summary:

  • Whatever you want to download is probably a Media object. (If it’s not already, construct one from a string with BlobMedia().)

  • Any Media object that can be retrieved from the server has a url attribute. Just set that URL as the url property of a Link component, and when you click on the link you will download the media.

  • To be downloadable from the server, the media object has to be stored somewhere. If you just create a BlobMedia from a string, that object’s url attribute will be None, because it only exists in the browser (or during one server call) - there’s nowhere to get it from when you click the link.

  • To make data downloadable, you can save it in a Media column of a data table row, or as a file in Google Drive. (Exporting a data table as CSV using the to_csv() method also produces a downloadable media object. You can also call to_csv() on a data table view.)

  • Advanced alternative: If you do want to generate data on the fly, rather than saving it somewhere, create an HTTP API endpoint that returns a Media object. Then use the URL for that API endpoint as the url property of your Link component.
    Note: If you hit an HTTP API endpoint from within a browser session - eg by clicking on a Link component - you can access the session state from the endpoint function. So, eg, if you’re logged into the Users service, the HTTP endpoint function can check anvil.users.get_user() to see whether the download should be permitted.

If you want to see this in action, check out the Secure Download Portal example. The files for download are stored in a Media column in a data table. The media from those columns is then used as the url for a Link object.

(Extra note: The url property of a Media object is a session-specific URL - for security, it will stop working after you’ve closed the app. If you want to make a long-lived URL you can access from other contexts, create an HTTP endpoint instead.)

I hope this helps!

2 Likes