-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Defered data for download button #5053
Comments
@Vinno97 Thanks for the suggestion. This would be indeed a nice addition to the download button, especially when dealing with large files. I will forward this feature request to our product team. |
In the meantime, I'm using this as a way of ensuring that page flow is not interrupted by large file prep
|
Yes agree! Back when we implemented download button, I know that we also thought about allowing users to pass a function. Not sure if we cut that just to reduce scope or if there were any reasons against doing that. Will revisit! |
I also had this issue, but it appears that it does approximately what you proposed, @Vinno97 ? Not sure if I'm missing some nuance with blocking when downloading large files, but I've already used this for data to be generated on click, regardless if it's data files or octet streams to be saved as files (eg: zip). Lifted from the docs: @st.cache
def convert_df(df):
# IMPORTANT: Cache the conversion to prevent computation on every rerun
return df.to_csv().encode('utf-8')
csv = convert_df(my_large_df)
st.download_button(
label="Download data as CSV",
data=csv,
file_name='large_df.csv',
mime='text/csv',
) @jrieke Was this functionality added in the meantime and not linked to this issue ? |
Nope we didn't implement this yet. We don't have a timeline yet but I'm 99 % sure we want to do this at some point. |
Any progress on this ? Do we have an ETA when this bug is gonna be fixed? |
I would appreciate if this gets resolved. I already tried to address this issue on the forum a couple months ago: https://discuss.streamlit.io/t/create-download-file-upon-clicking-a-button/32613 In my opinion, |
I'd also like to voice appreciation this feature. I finally tracked down my app's occasional hanging to this issue. In the meantime, gating the download button behind a "prepare data for download" button like @tomgallagher's example above is a clumsy but okay workaround. |
This would be a great feature. I know its highly requested, but when working with APIs, the lack of this feature makes it a miserable experience. It has to hit the API each time the page is reloaded to prep the download, meaning lots of requests within a quota are used up. Its even worse if you have multiple tabs on a page, each of which download a different dataset for the user - It means x api calls per page load, per tab, each time the script is rerun. Ive mitigated it by using a nested button like tom suggested, to 'get' data, then show the download button to download it, but a proper way to combine both into one UX Action would be amazing. |
+1 |
Same problem here. In my case I need to generate a excel file from multiple large pandas dataframes (one dataframe per sheet). I write the data as BytesIO. |
def get_data(): I added 'st.write("test")' in get_data, and found that "test"was printed before download_button. it means the get_data() still runs even download button is un-clicked. |
Unless there has been an update that hasn't been announced here, I'm not sure that a function can be called from st.download_button in this way. |
+1 to this feature, it'd be great for developers to create custom calculators that provide business value and a rich UX. |
any updates on this feature? |
+1 |
any updates on this feature? |
I came across this issue as well. Besides large data payloads being created on every run, it is annoying that there is no way to create the data only after the download button is clicked. Here is an example: create_data = st.button("Create data")
if "data" not in st.session_state:
st.session_state.data = None
st.download_button(
label="Export",
data=st.session_state.data,
file_name=file_name,
)
if create_data:
# logic to create data here
st.session_state.data = create_data_logic() Now, first I click on the "creata data" button and afterwards I click the "download" button but only None is downloaded. If the data creation process could happen in a callback after the download button is clicked, there would be no issue... Currently this workaround does the job for me, but I feel this should be natively possible in strewamlit without js hacks... |
Personally I want to say that Streamlit is very unpleasant for new users and I need to google every step and I continuously facing with issues with use cases. And yes, I want to +1 this bug too because when I want to download the data I want to click on the button, wait processing and get the data. |
Hi Team, We currently have the same issue and makes |
@sfc-gh-pkommini The only workaround I ever found is using two buttons as posted above. |
+1 on this issue. A super basic use-case is offering users a download of PNG images. This is a typical desire of a user if you want "archival quality" and are willing to eat the storage size - forcing people into JPEG all the time is not nice. PNG being mostly uncompressed means the filesize / data payload is going to be higher. Even moderately large PNG of dims 3072 x 4096 ends up being ~26 MB, which is totally feasible for generating in-memory and offering for one-off downloads. The ask is just to defer the costly serialization operations until the user actually clicks the download button, rather than having to do it every time just to display a download button. The workaround is too fiddly and requires too much ad-hoc state management to really be called a solution IMO. |
My team encountered this bug when apps are deployed in replicas to something like Kubernetes. |
2 buttons to do the job of 1 is not a suitable workaround. I end up have to have a save and export button when really I should just have an export button. Would be much appreciated if this was included, am very surprised it hasn't been already since being requested 2 years ago.. |
UpdateHey all! Sorry for not getting back to this issue for a while. We built a prototype of this last year. However, the implementation was a bit hacky and would have required a bigger effort to get right, so we decided not to pursue it further for the moment. Given that we recently released partial reruns via |
+1 |
1 similar comment
+1 |
Here is one way how to have button that will execute download function only when clicked. Using streamlit_javascript library but can be done also without it import base64
import time
import streamlit as st
from streamlit_javascript import st_javascript
def download_text(text, filename):
# long time process
time.sleep(3)
b64 = base64.b64encode(text.encode()).decode()
js_function = f"""(function() {{
var link = document.createElement('a');
link.href = 'data:text/plain;base64,{b64}';
link.download = '{filename}';
link.click();
}})();"""
st_javascript(js_function)
# adding of state is needed if you want to avoide empty div with iframe at the top of your page (flickers the page)
def trigger_download():
st.session_state["trigger_download"] = True
st.write("Execute download function only on click!")
st.button("Download Text", on_click=trigger_download)
if st.session_state.get("trigger_download", False):
st.session_state["trigger_download"] = False
download_text("This is the text content to download.", "example.txt") |
It seems this is a recurrent feature request. I too am looking for deferred pre-processing before the download is initiated. The main issue with the current approach is when you need to remotely download (or otherwise on-the-fly pre-process) large files. As of now these need to be loaded into memory, assuming caching is not a solution to get up-to-date data, which would make page loading slow and might not even be needed by the user. In my case that would be database backup exports: many large files, stored remotely, the user might or might not be interested in getting one of them... @jrieke You mentioned the use of Ideally, I'd like |
+1 |
6 similar comments
+1 |
+1 |
+1 |
+1 |
+1 |
+1 |
Problem
The download button currently expects its data to be available when declaring the button. If data needs to be read from disk (or worse: compiled multiple disk sources), this can make the app needlessly slow.
In my app, the data downloading is not a common use case, but the packing of the data for downloading is relatively expensive. Caching helps, but only when the data doesn't change.
Solution
I propose a method to only load and preprocess (archive, pickle, etc) when the download is actually requested.
I propose to also allow a function as a data type that gets called as soon as the download button is pressed. This callback then returns the actual data.
Possible additions:
Currently a download button accepts
str
,bytes
,TextIO
,BinaryIO
, orio.RawIOBase
. With deferred loading, it would also be possible to accept a file pointer and stream the data to the user. This might bring huge speed and memory benefits when downloading large files.Technically this streaming would also be possible without deferred loading, but then you're keeping unnecessary files open.
Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.
If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.
The text was updated successfully, but these errors were encountered: