I have a python that read files and convert it to dataframe using python and streamlit than i want to create a function to allow the user to download this dataframe as excel file with extension xls .
code:
import pandas as pd
import streamlit as st
writer=pd.ExcelWriter('update2.xlsx')
df.to_excel(writer, index = False, header=True,encoding='utf-8')
with open(writer,'rb') as f:
b64 = base64.b64encode(f.read())
href = f'<a href="data:file/xls;base64,{b64}" download="new_file.{extension}">Download {extension}</a>'
st.write(href, unsafe_allow_html=True)
When i try to download the file using a link the file doesn’t download and display this error:
Failed-Network error
where is the error in my code ??
Hi @leb_dev,
I was able to create a working example with the help of these two stackoverflow posts:
- Setting mime type for excel document - Stack Overflow
- java - How to decode an encoded excel file using python - Stack Overflow
import streamlit as st
import pandas as pd
import base64
import io
vals= ['A','B','C']
df = pd.DataFrame(vals, columns=["Title"])
df
towrite = io.BytesIO()
downloaded_file = df.to_excel(towrite, encoding='utf-8', index=False, header=True) # write to BytesIO buffer
towrite.seek(0) # reset pointer
b64 = base64.b64encode(towrite.read()).decode()
linko= f'<a href="data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,{b64}" download="myfilename.xlsx">Download excel file</a>'
st.markdown(linko, unsafe_allow_html=True)
Let us know if this works for you
Happy Streamlit-ing!
Snehan
1 Like
This is very handy and thank you.
I have put the above code into a function. So I want to create multiple links for different dfs. My code downloads the files with new filenames(correct) but is using the same dataframe(the last one referenced).
I am guessing that the io.BytesIO() lines need to be changed. But I am not sure how.
Any suggestions?
Thanks
Richard
def create_download_link(df, dfname):
towrite = io.BytesIO()
downloaded_file = df.to_excel(towrite, encoding='utf-8', index=False, header=True) # write to BytesIO buffer
towrite.seek(0) # reset pointer
b64 = base64.b64encode(towrite.read()).decode()
fn = dfname + '.xlsx'
#linko= f'<a href="data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,{b64}" download="myfilename.xlsx">Download excel file</a>'
linko= f'<a href="data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,{b64}" download=' + fn + '>' + fn + '</a>'
return(linko)
st.markdown(create_download_link(df, "df"), unsafe_allow_html=True)
st.markdown(create_download_link(df2, "df2"), unsafe_allow_html=True)
@Richard_Fitzsimons Suggest you close() the towrite
stream after the b64 encoding.
:
b64 = base64.b64encode(towrite.read()).decode()
towrite.close()
:
1 Like
Thanks for your answer. In fact the problem was elsewhere. I had made a shallow copy of my dataframe (for testing) : df2=df1
When I modified df2, df1 was also changed.
I changed to
df2 = df1.copy(deep=True)
Now I have an independent df and everything works.
Currently there is no difference if I add in towrite.close() or not.
Cheers
Richard
1 Like