File size: 3,336 Bytes
efbe6b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import io
import re
import uuid
import base64
import streamlit as st
from typing import Optional, Union
from streamlit.elements.button import DownloadButtonDataType

DownloadButtonDataType = Union[DownloadButtonDataType, "pd.DataFrame", "Styler"]

HAS_PD = True


def download_button(label: str,
                    data: DownloadButtonDataType,
                    file_name: Optional[str] = None) -> str:
    """Generates a link to download the given data, support file-like object and pd.DataFrame.
    Params
    Args:
        label: text show on page.
        data: file-like object or pd.DataFrame.
        file_name: filename and extension of file. e.g. mydata.csv,
    Raises:
        RuntimeError: when data type is not supported
    Returns:
        the anchor tag to download object_to_download
    Examples:
        download_button('Click to download data!', your_df, 'YOUR_DF.xlsx'),
        download_button('Click to download text!', your_str.encode(), 'YOUR_STRING.txt')
    """

    # inspired by https://gist.github.com/chad-m/6be98ed6cf1c4f17d09b7f6e5ca2978f

    data_as_bytes: bytes
    if isinstance(data, str):
        data_as_bytes = data.encode()
    elif isinstance(data, io.TextIOWrapper):
        string_data = data.read()
        data_as_bytes = string_data.encode()
        # mimetype = mimetype or "text/plain"
    # Assume bytes; try methods until we run out.
    elif isinstance(data, bytes):
        data_as_bytes = data
    elif isinstance(data, io.BytesIO):
        data.seek(0)
        data_as_bytes = data.getvalue()
    elif isinstance(data, io.BufferedReader):
        data.seek(0)
        data_as_bytes = data.read()
    elif isinstance(data, io.RawIOBase):
        data.seek(0)
        data_as_bytes = data.read() or b""
    elif HAS_PD and hasattr(data, "to_excel"):
        bio = io.BytesIO()
        data.to_excel(bio)
        bio.seek(0)
        data_as_bytes = bio.read()
    else:
        raise RuntimeError("Invalid binary data format: %s" % type(data))

    b64 = base64.b64encode(data_as_bytes).decode()
    button_uuid = str(uuid.uuid4()).replace("-", "")
    button_id = re.sub(r"\d+", "", button_uuid)

    custom_css = f"""
        <style>
            #{button_id} {{
                background-color: rgb(255, 255, 255);
                color: rgb(38, 39, 48);
                padding: 0.4em 0.6em;
                position: relative;
                text-decoration: none;
                border-radius: 4px;
                border-width: 1px;
                border-style: solid;
                border-color: rgba(49, 51, 63, 0.2);
                border-image: initial;
            }}
            #{button_id}:hover {{
                border-color: rgb(246, 51, 102);
                color: rgb(246, 51, 102);
            }}
            #{button_id}:active {{
                box-shadow: none;
                background-color: rgb(246, 51, 102);
                color: white;
                }}
        </style> """

    dl_link = (
            custom_css
            + f'<a class="steDownloadButton" download="{file_name}" id="{button_id}" '
              f'href="data:file/txt;base64,{b64}">{label}</a><br></br>'
    )

    div_dl_link = f"""<div class="row-widget stDownloadButton">{dl_link}</div>"""
    st.markdown(div_dl_link, unsafe_allow_html=True)
    return dl_link