示例#1
0
    def file_uploader(
        self,
        label: str,
        type: Optional[Union[str, List[str]]] = None,
        accept_multiple_files: bool = False,
        key: Optional[Key] = None,
        help: Optional[str] = None,
        on_change: Optional[WidgetCallback] = None,
        args: Optional[WidgetArgs] = None,
        kwargs: Optional[WidgetKwargs] = None,
    ) -> SomeUploadedFiles:
        """Display a file uploader widget.
        By default, uploaded files are limited to 200MB. You can configure
        this using the `server.maxUploadSize` config option. For more info
        on how to set config options, see
        https://docs.streamlit.io/library/advanced-features/configuration#set-configuration-options

        Parameters
        ----------
        label : str
            A short label explaining to the user what this file uploader is for.

        type : str or list of str or None
            Array of allowed extensions. ['png', 'jpg']
            The default is None, which means all extensions are allowed.

        accept_multiple_files : bool
            If True, allows the user to upload multiple files at the same time,
            in which case the return value will be a list of files.
            Default: False

        key : str or int
            An optional string or integer 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.

        help : str
            A tooltip that gets displayed next to the file uploader.

        on_change : callable
            An optional callback invoked when this file_uploader's value
            changes.

        args : tuple
            An optional tuple of args to pass to the callback.

        kwargs : dict
            An optional dict of kwargs to pass to the callback.

        Returns
        -------
        None or UploadedFile or list of UploadedFile
            - If accept_multiple_files is False, returns either None or
              an UploadedFile object.
            - If accept_multiple_files is True, returns a list with the
              uploaded files as UploadedFile objects. If no files were
              uploaded, returns an empty list.

            The UploadedFile class is a subclass of BytesIO, and therefore
            it is "file-like". This means you can pass them anywhere where
            a file is expected.

        Examples
        --------
        Insert a file uploader that accepts a single file at a time:

        >>> uploaded_file = st.file_uploader("Choose a file")
        >>> if uploaded_file is not None:
        ...     # To read file as bytes:
        ...     bytes_data = uploaded_file.getvalue()
        ...     st.write(bytes_data)
        >>>
        ...     # To convert to a string based IO:
        ...     stringio = StringIO(uploaded_file.getvalue().decode("utf-8"))
        ...     st.write(stringio)
        >>>
        ...     # To read file as string:
        ...     string_data = stringio.read()
        ...     st.write(string_data)
        >>>
        ...     # Can be used wherever a "file-like" object is accepted:
        ...     dataframe = pd.read_csv(uploaded_file)
        ...     st.write(dataframe)

        Insert a file uploader that accepts multiple files at a time:

        >>> uploaded_files = st.file_uploader("Choose a CSV file", accept_multiple_files=True)
        >>> for uploaded_file in uploaded_files:
        ...     bytes_data = uploaded_file.read()
        ...     st.write("filename:", uploaded_file.name)
        ...     st.write(bytes_data)
        """
        key = to_key(key)
        check_callback_rules(self.dg, on_change)
        check_session_state_rules(default_value=None,
                                  key=key,
                                  writes_allowed=False)

        if type:
            if isinstance(type, str):
                type = [type]

            # May need a regex or a library to validate file types are valid
            # extensions.
            type = [
                file_type if file_type[0] == "." else f".{file_type}"
                for file_type in type
            ]

        file_uploader_proto = FileUploaderProto()
        file_uploader_proto.label = label
        file_uploader_proto.type[:] = type if type is not None else []
        file_uploader_proto.max_upload_size_mb = config.get_option(
            "server.maxUploadSize")
        file_uploader_proto.multiple_files = accept_multiple_files
        file_uploader_proto.form_id = current_form_id(self.dg)
        if help is not None:
            file_uploader_proto.help = dedent(help)

        def deserialize_file_uploader(
                ui_value: Optional[FileUploaderStateProto],
                widget_id: str) -> SomeUploadedFiles:
            file_recs = self._get_file_recs(widget_id, ui_value)
            if len(file_recs) == 0:
                return_value: Optional[Union[List[UploadedFile],
                                             UploadedFile]] = (
                                                 [] if accept_multiple_files
                                                 else None)
            else:
                files = [UploadedFile(rec) for rec in file_recs]
                return_value = files if accept_multiple_files else files[0]
            return return_value

        def serialize_file_uploader(
                files: SomeUploadedFiles) -> FileUploaderStateProto:
            state_proto = FileUploaderStateProto()

            ctx = get_report_ctx()
            if ctx is None:
                return state_proto

            # ctx.uploaded_file_mgr._file_id_counter stores the id to use for
            # the *next* uploaded file, so the current highest file id is the
            # counter minus 1.
            state_proto.max_file_id = ctx.uploaded_file_mgr._file_id_counter - 1

            if not files:
                return state_proto
            elif not isinstance(files, list):
                files = [files]

            for f in files:
                file_info: UploadedFileInfoProto = state_proto.uploaded_file_info.add(
                )
                file_info.id = f.id
                file_info.name = f.name
                file_info.size = f.size

            return state_proto

        # FileUploader's widget value is a list of file IDs
        # representing the current set of files that this uploader should
        # know about.
        widget_value, _ = register_widget(
            "file_uploader",
            file_uploader_proto,
            user_key=key,
            on_change_handler=on_change,
            args=args,
            kwargs=kwargs,
            deserializer=deserialize_file_uploader,
            serializer=serialize_file_uploader,
        )

        ctx = get_report_ctx()
        file_uploader_state = serialize_file_uploader(widget_value)
        uploaded_file_info = file_uploader_state.uploaded_file_info
        if ctx is not None and len(uploaded_file_info) != 0:
            newest_file_id = file_uploader_state.max_file_id
            active_file_ids = [f.id for f in uploaded_file_info]

            ctx.uploaded_file_mgr.remove_orphaned_files(
                session_id=ctx.session_id,
                widget_id=file_uploader_proto.id,
                newest_file_id=newest_file_id,
                active_file_ids=active_file_ids,
            )

        self.dg._enqueue("file_uploader", file_uploader_proto)
        return cast(SomeUploadedFiles, widget_value)
    def file_uploader(dg,
                      label,
                      type=None,
                      accept_multiple_files=False,
                      key=None,
                      **kwargs):
        """Display a file uploader widget.
        By default, uploaded files are limited to 200MB. You can configure
        this using the `server.maxUploadSize` config option.

        Parameters
        ----------
        label : str or None
            A short label explaining to the user what this file uploader is for.

        type : str or list of str or None
            Array of allowed extensions. ['png', 'jpg']
            The default is None, which means all extensions are allowed.

        accept_multiple_files : bool
            If True, allows the user to upload multiple files at the same time,
            in which case the return value will be a list of files.
            Default: False

        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
        -------
        None or UploadedFile or list of UploadedFile
            - If allow_multiple_files is False, returns either None or
              an UploadedFile object.
            - If allow_multiple_files is True, returns a list with the
              uploaded files as UploadedFile objects. If no files were
              uploaded, returns an empty list.

            The UploadedFile class is a subclass of BytesIO, and therefore
            it is "file-like". This means you can pass them anywhere where
            a file is expected. As a subclass of BytesIO, make sure to
            reset the buffer after reading it with `UploadedFile.seek(0)`.

        Examples
        --------
        Insert a file uploader that accepts a single file at a time:

        >>> uploaded_file = st.file_uploader("Choose a file")
        >>> if uploaded_file is not None:
        ...     # To read file as bytes:
        ...     bytes_data = uploaded_file.read()
        ...     st.write(bytes_data)
        >>>
        ...     # To convert to a string based IO:
        ...     stringio = StringIO(uploaded_file.decode("utf-8"))
        ...     st.write(stringio)
        >>>
        ...     # To read file as string:
        ...     string_data = stringio.read()
        ...     st.write(string_data)
        >>>
        ...     # Can be used wherever a "file-like" object is accepted:
        ...     dataframe = pd.read_csv(uploaded_file)
        ...     st.write(dataframe)

        Insert a file uploader that accepts multiple files at a time:

        >>> uploaded_files = st.file_uploader("Choose a CSV file", accept_multiple_files=True)
        >>> for uploaded_file in uploaded_files:
        ...     bytes_data = uploaded_file.read()
        ...     st.write("filename:", uploaded_file.name)
        ...     st.write(bytes_data)
        """

        if type:
            if isinstance(type, str):
                type = [type]

            # May need a regex or a library to validate file types are valid
            # extensions.
            type = [
                file_type if file_type[0] == "." else f".{file_type}"
                for file_type in type
            ]

        has_encoding = "encoding" in kwargs
        show_deprecation_warning = config.get_option(
            "deprecation.showfileUploaderEncoding")

        if show_deprecation_warning and has_encoding:
            dg.exception(FileUploaderEncodingWarning())  # type: ignore

        file_uploader_proto = FileUploaderProto()
        file_uploader_proto.label = label
        file_uploader_proto.type[:] = type if type is not None else []
        file_uploader_proto.max_upload_size_mb = config.get_option(
            "server.maxUploadSize")
        file_uploader_proto.multiple_files = accept_multiple_files
        _set_widget_id("file_uploader", file_uploader_proto, user_key=key)

        files = None
        ctx = get_report_ctx()
        if ctx is not None:
            files = ctx.uploaded_file_mgr.get_files(
                session_id=ctx.session_id, widget_id=file_uploader_proto.id)

        if files is None or len(files) == 0:
            return_value = [] if accept_multiple_files else NoValue
        else:
            for file in files:
                if file.tell() > 0:
                    file.seek(0)
            return_value = files if accept_multiple_files else files[0]

        return dg._enqueue("file_uploader", file_uploader_proto,
                           return_value)  # type: ignore
示例#3
0
    def file_uploader(
        self, label, type=None, accept_multiple_files=False, key=None, help=None
    ):
        """Display a file uploader widget.
        By default, uploaded files are limited to 200MB. You can configure
        this using the `server.maxUploadSize` config option.

        Parameters
        ----------
        label : str
            A short label explaining to the user what this file uploader is for.

        type : str or list of str or None
            Array of allowed extensions. ['png', 'jpg']
            The default is None, which means all extensions are allowed.

        accept_multiple_files : bool
            If True, allows the user to upload multiple files at the same time,
            in which case the return value will be a list of files.
            Default: False

        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.

        help : str
            A tooltip that gets displayed next to the file uploader.

        Returns
        -------
        None or UploadedFile or list of UploadedFile
            - If accept_multiple_files is False, returns either None or
              an UploadedFile object.
            - If accept_multiple_files is True, returns a list with the
              uploaded files as UploadedFile objects. If no files were
              uploaded, returns an empty list.

            The UploadedFile class is a subclass of BytesIO, and therefore
            it is "file-like". This means you can pass them anywhere where
            a file is expected.

        Examples
        --------
        Insert a file uploader that accepts a single file at a time:

        >>> uploaded_file = st.file_uploader("Choose a file")
        >>> if uploaded_file is not None:
        ...     # To read file as bytes:
        ...     bytes_data = uploaded_file.getvalue()
        ...     st.write(bytes_data)
        >>>
        ...     # To convert to a string based IO:
        ...     stringio = StringIO(uploaded_file.getvalue().decode("utf-8"))
        ...     st.write(stringio)
        >>>
        ...     # To read file as string:
        ...     string_data = stringio.read()
        ...     st.write(string_data)
        >>>
        ...     # Can be used wherever a "file-like" object is accepted:
        ...     dataframe = pd.read_csv(uploaded_file)
        ...     st.write(dataframe)

        Insert a file uploader that accepts multiple files at a time:

        >>> uploaded_files = st.file_uploader("Choose a CSV file", accept_multiple_files=True)
        >>> for uploaded_file in uploaded_files:
        ...     bytes_data = uploaded_file.read()
        ...     st.write("filename:", uploaded_file.name)
        ...     st.write(bytes_data)
        """

        if type:
            if isinstance(type, str):
                type = [type]

            # May need a regex or a library to validate file types are valid
            # extensions.
            type = [
                file_type if file_type[0] == "." else f".{file_type}"
                for file_type in type
            ]

        file_uploader_proto = FileUploaderProto()
        file_uploader_proto.label = label
        file_uploader_proto.type[:] = type if type is not None else []
        file_uploader_proto.max_upload_size_mb = config.get_option(
            "server.maxUploadSize"
        )
        file_uploader_proto.multiple_files = accept_multiple_files
        if help is not None:
            file_uploader_proto.help = help
        register_widget("file_uploader", file_uploader_proto, user_key=key)

        # FileUploader's widget value is a list of file IDs
        # representing the current set of files that this uploader should
        # know about.
        widget_value: Optional[SInt64Array] = register_widget(
            "file_uploader", file_uploader_proto, user_key=key
        )

        file_recs = self._get_file_recs(file_uploader_proto.id, widget_value)

        if len(file_recs) == 0:
            return_value = [] if accept_multiple_files else NoValue
        else:
            files = [UploadedFile(rec) for rec in file_recs]
            return_value = files if accept_multiple_files else files[0]

        return self.dg._enqueue("file_uploader", file_uploader_proto, return_value)
示例#4
0
    def _file_uploader(
        self,
        label: str,
        type: Optional[Union[str, List[str]]] = None,
        accept_multiple_files: bool = False,
        key: Optional[Key] = None,
        help: Optional[str] = None,
        on_change: Optional[WidgetCallback] = None,
        args: Optional[WidgetArgs] = None,
        kwargs: Optional[WidgetKwargs] = None,
        *,  # keyword-only arguments:
        disabled: bool = False,
        ctx: Optional[ScriptRunContext] = None,
    ):
        key = to_key(key)
        check_callback_rules(self.dg, on_change)
        check_session_state_rules(default_value=None,
                                  key=key,
                                  writes_allowed=False)

        if type:
            if isinstance(type, str):
                type = [type]

            # May need a regex or a library to validate file types are valid
            # extensions.
            type = [
                file_type if file_type[0] == "." else f".{file_type}"
                for file_type in type
            ]

        file_uploader_proto = FileUploaderProto()
        file_uploader_proto.label = label
        file_uploader_proto.type[:] = type if type is not None else []
        file_uploader_proto.max_upload_size_mb = config.get_option(
            "server.maxUploadSize")
        file_uploader_proto.multiple_files = accept_multiple_files
        file_uploader_proto.form_id = current_form_id(self.dg)
        file_uploader_proto.disabled = disabled
        if help is not None:
            file_uploader_proto.help = dedent(help)

        def deserialize_file_uploader(
                ui_value: Optional[FileUploaderStateProto],
                widget_id: str) -> SomeUploadedFiles:
            file_recs = self._get_file_recs(widget_id, ui_value)
            if len(file_recs) == 0:
                return_value: Optional[Union[List[UploadedFile],
                                             UploadedFile]] = (
                                                 [] if accept_multiple_files
                                                 else None)
            else:
                files = [UploadedFile(rec) for rec in file_recs]
                return_value = files if accept_multiple_files else files[0]
            return return_value

        def serialize_file_uploader(
                files: SomeUploadedFiles) -> FileUploaderStateProto:
            state_proto = FileUploaderStateProto()

            ctx = get_script_run_ctx()
            if ctx is None:
                return state_proto

            # ctx.uploaded_file_mgr._file_id_counter stores the id to use for
            # the *next* uploaded file, so the current highest file id is the
            # counter minus 1.
            state_proto.max_file_id = ctx.uploaded_file_mgr._file_id_counter - 1

            if not files:
                return state_proto
            elif not isinstance(files, list):
                files = [files]

            for f in files:
                file_info: UploadedFileInfoProto = state_proto.uploaded_file_info.add(
                )
                file_info.id = f.id
                file_info.name = f.name
                file_info.size = f.size

            return state_proto

        # FileUploader's widget value is a list of file IDs
        # representing the current set of files that this uploader should
        # know about.
        widget_value, _ = register_widget(
            "file_uploader",
            file_uploader_proto,
            user_key=key,
            on_change_handler=on_change,
            args=args,
            kwargs=kwargs,
            deserializer=deserialize_file_uploader,
            serializer=serialize_file_uploader,
            ctx=ctx,
        )

        file_uploader_state = serialize_file_uploader(widget_value)
        uploaded_file_info = file_uploader_state.uploaded_file_info
        if ctx is not None and len(uploaded_file_info) != 0:
            newest_file_id = file_uploader_state.max_file_id
            active_file_ids = [f.id for f in uploaded_file_info]

            ctx.uploaded_file_mgr.remove_orphaned_files(
                session_id=ctx.session_id,
                widget_id=file_uploader_proto.id,
                newest_file_id=newest_file_id,
                active_file_ids=active_file_ids,
            )

        self.dg._enqueue("file_uploader", file_uploader_proto)
        return cast(SomeUploadedFiles, widget_value)
    def file_uploader(dg, label, type=None, key=None, **kwargs):
        """Display a file uploader widget.

        By default, uploaded files are limited to 200MB. You can configure
        this using the `server.maxUploadSize` config option.

        Parameters
        ----------
        label : str or None
            A short label explaining to the user what this file uploader is for.
        type : str or list of str or None
            Array of allowed extensions. ['png', 'jpg']
            By default, all extensions are allowed.
        encoding : str or None
            The encoding to use when opening textual files (i.e. non-binary).
            For example: 'utf-8'. If set to 'auto', will try to guess the
            encoding. If None, will assume the file is binary.
        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
        -------
        BytesIO or StringIO or or list of BytesIO/StringIO or None
            If no file has been uploaded, returns None. Otherwise, returns
            the data for the uploaded file(s):
            - If the file is in a well-known textual format (or if the encoding
            parameter is set), the file data is a StringIO.
            - Otherwise the file data is BytesIO.
            - If multiple_files is True, a list of file data will be returned.

            Note that BytesIO/StringIO are "file-like", which means you can
            pass them anywhere where a file is expected!

        Examples
        --------
        >>> uploaded_file = st.file_uploader("Choose a CSV file", type="csv")
        >>> if uploaded_file is not None:
        ...     data = pd.read_csv(uploaded_file)
        ...     st.write(data)

        """
        # Don't release this just yet. (When ready to release, turn test back
        # on at file_uploader_test.py)
        accept_multiple_files = False

        if isinstance(type, str):
            type = [type]

        encoding = kwargs.get("encoding")
        has_encoding = "encoding" in kwargs
        show_deprecation_warning = config.get_option(
            "deprecation.showfileUploaderEncoding")

        if show_deprecation_warning and (
            (has_encoding and encoding is not None) or not has_encoding):
            dg.exception(FileUploaderEncodingWarning())  # type: ignore

        if not has_encoding:
            encoding = "auto"

        file_uploader_proto = FileUploaderProto()
        file_uploader_proto.label = label
        file_uploader_proto.type[:] = type if type is not None else []
        file_uploader_proto.max_upload_size_mb = config.get_option(
            "server.maxUploadSize")
        file_uploader_proto.multiple_files = accept_multiple_files
        _set_widget_id("file_uploader", file_uploader_proto, user_key=key)

        files = None
        ctx = get_report_ctx()
        if ctx is not None:
            files = ctx.uploaded_file_mgr.get_files(
                session_id=ctx.session_id, widget_id=file_uploader_proto.id)

        if files is None:
            return_value = NoValue
        else:
            file_datas = [
                get_encoded_file_data(file.data, encoding) for file in files
            ]
            return_value = file_datas if accept_multiple_files else file_datas[
                0]
        return dg._enqueue("file_uploader", file_uploader_proto,
                           return_value)  # type: ignore