Create a Download Button API to download files
See original GitHub issueProblem
I’m hoping to be able to provide a Streamlit function that allows for the downloading of files. There are a few solutions in the wild at the moment that I know of, each with its own downsides:
- https://github.com/streamlit/streamlit/issues/400#issuecomment-648580840
- Doesn’t work on https://share.streamlit.io since this requires mutation of a write protected Streamlit python package directory.
- https://github.com/streamlit/streamlit/issues/400#issuecomment-564935632
- Doesn’t work for files larger than 50 MB, requires the time cost of converting to base64 and having that be written onto the HTML page.
- https://github.com/streamlit/streamlit/issues/400#issuecomment-548629786
- Won’t work on https://share.streamlit.io since it requires a separate server.
- https://gist.github.com/tconkling/1e5ead87c796a82de7fa71fcc4a74777
- Won’t work on https://share.streamlit.io since it requires editing Streamlit before the server boots.
Solution
API Details
streamlit.download_button(label, data, file_name='file', mime=None, key=None)
Display a button to download data to the user’s machine.
Parameters
label
(str
) – A short label explaining to the user what this button is for.data
(str
orbytes
orfile
) – The contents of the file to be downloaded.file_name
(str
) - An optional string to use as the name of the file to be downloaded, eg. ‘my_file.csv’. If not file name is specified, then we provide a generic name for the download.mime
(str
orNone
) – The MIME type of the data. If None, defaults to “text/plain” or “application/octet-stream” depending on the data type.key
(str
) – An optional string to use as the unique key for the widget. If this is omitted, a key will be generated for the widget based on its content. Multiple widgets of the same type may not share the same key.
Returns
True if the button was clicked on the last run of the app. False otherwise.
Return type
bool
Example Usage
# Text files
text_contents = '''
Foo, Bar
123, 456
789, 000
'''
# Different ways to use the API
st.download_button('Download CSV', text_contents, 'text/csv')
st.download_button('Download CSV', text_contents) # Defaults to 'text/plain'
with open('myfile.csv', 't') as f:
st.download_button('Download CSV', f) # Defaults to 'text/plain'
# ---
# Binary files
binary_contents = b'whatever'
# Different ways to use the API
st.download_button('Download file', binary_contents) # Defaults to 'application/octet-stream'
with open('myfile.zip', 'b') as f:
st.download_button('Download Zip', f) # Defaults to 'application/octet-stream'
# You can also grab the return value of the button,
# just like with any other button.
if st.download_button(...):
st.write('Thanks for downloading!')
Implementation
TLDR
- Extend the Button Widget with download capabilities
- Use the MediaFileManager to store the file for downloading.
Plan
Implementation of the download button requires two parts:
- Implement the Proto/Frontend Button Widget to accept a download URL and file name and download the URL specified.
- Implementing the API on the Python side to create a button. Essentially, this will be very similar code to
st.button
, but it must do a few things differently:- Store the file information somewhere on the server
- Instruct the frontend that it’s a download button as well as a URL to download the button from.
- Proper clean up of the file information on rerun.
Tasks
Can be done in the following order to be done in the mainline develop
branch with no issues. Overall, I see 2-3 tasks:
- Implement the proto/frontend to test for a download button.
- Extend the proto with a
download_url
anddownload_filename
default the Python button to set that equal toNone
- Extend the same Button Widget to include the added feature for download url. If it exists, download the url by creating a shadow link. See The
VideoRecordedDialog
event handler. - Unit Tests for the button with the new functionality.
- Verification of e2e tests that standard buttons are unchanged.
- Extend the proto with a
- Implement the Python API
- API meets spec.
- Properly documented and approved by documentation.
- Add file bytes to a location theMediaFileManager
- This begins to break the paradigm of the MediaFileManager (focus on media), but routes are provided and there’s no real reason why it focuses on media. It’s more or less a file type. A task to “generalize” MediaFileManager to be FileManager can be created and done beforehand or done if the amount of work is small.
- Full E2E tests for the download button possible.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:12
- Comments:18 (11 by maintainers)
Top Results From Across the Web
How to trigger a file download when clicking an HTML button ...
To trigger a file download on a button click we will use a custom function or HTML 5 download attribute. ... The download...
Read more >How to trigger a file download when clicking ... - Stack Overflow
You can trigger a download with the HTML5 download attribute. <a href="path_to_file" download="proposed_file_name">Download</a>. Where:.
Read more >Programmatic file downloads in the browser - LogRocket Blog
So far, we have looked at how we can download files that are served from a server and sent to the client over...
Read more >Download Files using Web API - codeburst
In this article, I will use a demo Web API application in ASP.NET Core to show you how to transmit files through an...
Read more >Download file on button click in the Browser with Javascript
Let's create the Download Button. As we already know what's a Blob, let's have a look at how to download any data just...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
@SimonBiggs : As the PM for this feature, sharing some of my thoughts 🙂
Having the
filename
before the mime type makes sense. Good call!As a starting point, I would prefer we restrict the scope and ask the user to specify the MIME type. Definitely open to auto-detection in the future, so we can look into the magic functionality in v2.0.
Hi @nthmost 🙂,
Who’s the best person to ping to get permission to make a PR? Is this still the best way to go about it? Or might it be better that I just start the PR?
Cheers 😊 @SimonBiggs