How to download file in streamlit

not directly, but there is a workaround. Its discussed in the following contribution: File Download Workaround added to awesome-streamlit.org.

I have created the following function for myself and use it frequently. It works fine for bigger files too, my files often have thousands of rows and so far I havenā€™t hit a limit yet.

st.markdown(get_table_download_link(df), unsafe_allow_html=True)
def get_table_download_link(df):
    """Generates a link allowing the data in a given panda dataframe to be downloaded
    in:  dataframe
    out: href string
    """
    csv = df.to_csv(index=False)
    b64 = base64.b64encode(csv.encode()).decode()  # some strings <-> bytes conversions necessary here
    href = f'<a href="data:file/csv;base64,{b64}">Download csv file</a>'
15 Likes

Thanks!! Canā€™t imagine I missed this post :sweat:ļ¼ŒI should search more carefully before I post next time

2 Likes

hey using that opens a new tab with title about:blank#blocked
and nothing happens

please help

1 Like

This works for me, just make sure to return href at the bottom of the get_table_download_link function

3 Likes

Heya, this works for me, but the files download without the .csv extension. Is there a way to make sure that the .csv extension occurs or specify the name of the csv file?

Thank you!

1 Like

I havenā€™t found a way to do that automatically, but one can always edit the filename to filename.csv when saving the link with the right mouse click.

2 Likes

@marciorpcoelho I see, thank you for putting this workaround together it is very useful!

1 Like

Why did U say that the file is downloaded without the .csv, itā€™s not true if you have converted your dataframe with the to_csv() method. Itā€™s works like a charm

1 Like

Adding the ā€˜downloadā€™ tag attribute as shown below allows you to provide a file name and extension.

f'<a href="data:file/csv;base64,{b64}" download="myfilename.csv">Download csv file</a>'

18 Likes

Welcome to the community @dplutchok, and thanks for pointing out the download argument!

4 Likes

Iā€™ve been searching for a while, a way to do same thing with excel xlsx file. I finally found
a way to make it work, so I decide to share my code here. I hope it will help someone.

python 3, pandas '0.23.4'

import streamlit as st
import base64
from io import BytesIO

def to_excel(df):
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    df.to_excel(writer, sheet_name='Sheet1')
    writer.save()
    processed_data = output.getvalue()
    return processed_data

def get_table_download_link(df):
    """Generates a link allowing the data in a given panda dataframe to be downloaded
    in:  dataframe
    out: href string
    """
    val = to_excel(df)
    b64 = base64.b64encode(val)  # val looks like b'...'
    return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="extract.xlsx">Download csv file</a>' # decode b'abc' => abc

df = ... # your dataframe
st.markdown(get_table_download_link(df), unsafe_allow_html=True)

# PS: pip install xlsxwriter  # pandas need this
20 Likes

This is a great answer for csv files or excel files.
However does it work on midi (*.mid) files? midi files are binary. like the excel files.

Now I dont think i need to encode/decode to b64. what do you think? if i do use the b64 enc/decoding i get

TypeError: a bytes-like object is required, not '_io.BufferedReaderā€™

if i dont it still doesnt work.
you can find an example midi file here example_midi

example:

import base64
def get_midi_download_link(mid):
    """Generates a link allowing the data in a given midi file to be downloaded
    in:  midi file
    out: href string
    """
    
      # val looks like b'...'
    return f'<a href="data:application/octet-stream;{mid}" download="example.mid">Download midi file with save as</a>' 




with open("105027.mid", 'rb') as inmidi:
    st.markdown(get_midi_download_link(inmidi), unsafe_allow_html=True)
3 Likes

Whenever you download the .xlsx file, index column will come in the sheet1. How we can make index = False? Could you please help me out in this?

1 Like

Hello @goutamborthakur555, welcome to the community :slight_smile: !

I believe from the to_excel docs that index=False should be added to the to_excel function so :

def to_excel(df):
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    df.to_excel(writer, index=False, sheet_name='Sheet1') # <--- here
    writer.save()
    processed_data = output.getvalue()
    return processed_data

Fanilo

4 Likes

Thank you!

1 Like

Hi, if I make an excel file downloadable and if my excel downloadable file values are in float of 4 decimal place, how to make it to 2 decimal place? i.e while download the file values should come with 2 decimal places.

import streamlit as st
import base64
from io import BytesIO

def to_excel(df):
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    df.to_excel(writer, index = False, sheet_name='Sheet1')
    workbook  = writer.book
    worksheet = writer.sheets['Sheet1']
    format1 = workbook.add_format({'num_format': '0.00'}) # Tried with '0%' and '#,##0.00' also.
    worksheet.set_column('A:A', None, format1) # Say Data are in column A
    writer.save()
    processed_data = output.getvalue()
    return processed_data

def get_table_download_link(df):
    """Generates a link allowing the data in a given panda dataframe to be downloaded
    in:  dataframe
    out: href string
    """
    val = to_excel(df)
    b64 = base64.b64encode(val)  # val looks like b'...'
    return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="Your_File.xlsx">Download Excel file</a>' # decode b'abc' => abc

st.markdown(get_table_download_link(df), unsafe_allow_html=True)

df = my dataframe

Position
5.7680
2.7680
5.7680
5.7680
7.2680
5.7680
5.7680
5.7680
5.7680
5.7680
5.7680
5.7680
5.9680
5.7680
2.7680
5.7680
5.7680
5.7680
5.7680
5.7680
1 Like

Thank, I got the answer. Was supposed this: float_format="%.2f"

import streamlit as st
import base64
from io import BytesIO

def to_excel(df):
    output = BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    df.to_excel(writer, index = False, sheet_name='Sheet1',float_format="%.2f")
    writer.save()
    processed_data = output.getvalue()
    return processed_data

def get_table_download_link(df):
    """Generates a link allowing the data in a given panda dataframe to be downloaded
    in:  dataframe
    out: href string
    """
    val = to_excel(df)
    b64 = base64.b64encode(val)  # val looks like b'...'
    return f'<a href="data:application/octet-stream;base64,{b64.decode()}" download="Your_File.xlsx">Download Excel file</a>' # decode b'abc' => abc

st.markdown(get_table_download_link(df), unsafe_allow_html=True)
2 Likes

In case if you need to download image:

result = Image.fromarray(predicted_img)

def get_image_download_link(img):
	"""Generates a link allowing the PIL image to be downloaded
	in:  PIL image
	out: href string
	"""
	buffered = BytesIO()
	img.save(buffered, format="JPEG")
	img_str = base64.b64encode(buffered.getvalue()).decode()
	href = f'<a href="data:file/jpg;base64,{img_str}">Download result</a>'
	return href

st.markdown(get_image_download_link(result), unsafe_allow_html=True)

This code assumes that you convert you image to PIL format from the numpy array.

5 Likes

If it helps anyone, I posted a version of the function that can handle both dataframes and text: Here's a download function that works for dataframes and txt (posted below for reference)

Also, curious if anyone has any ideas about how to generalize file downloads in Streamlit. For example: CSV, Excel, img, txt, midi, pdf, video formats, etc.

It would also be neat to auto download on button click - or interaction with another Streamlit component - rather than a link being returned. Iā€™ve started researching ways to achieve this, but itā€™s admittedly not my specialty area. Any thoughts on how to achieve this?

import base64

import streamlit as st
import pandas as pd


def download_link(object_to_download, download_filename, download_link_text):
    """
    Generates a link to download the given object_to_download.

    object_to_download (str, pd.DataFrame):  The object to be downloaded.
    download_filename (str): filename and extension of file. e.g. mydata.csv, some_txt_output.txt
    download_link_text (str): Text to display for download link.

    Examples:
    download_link(YOUR_DF, 'YOUR_DF.csv', 'Click here to download data!')
    download_link(YOUR_STRING, 'YOUR_STRING.txt', 'Click here to download your text!')

    """
    if isinstance(object_to_download,pd.DataFrame):
        object_to_download = object_to_download.to_csv(index=False)

    # some strings <-> bytes conversions necessary here
    b64 = base64.b64encode(object_to_download.encode()).decode()
4 Likes

Yeah Iā€™m trying to find a way to include a download button for a pdf using Streamlit too.

1 Like