Component: Upload Multiple Files (WIP)

Hi Team

I want to be able to upload multiple files. My knowledge in react is lacking but I seen many react packages that handle multiple files upload such as react-dropzone react-dropzone-uploader etcā€¦

Iā€™m struggling to implement them with Streamlit. Ideally, I want to upload a bunch of files directly to s3 and then just use st.image to preview them or get the list of images as output of react component and then us st.image image to preview them without having to upload to cloud.

If anyone can point me in the right direction it will be nice.

So far I can log the accepted files but not sure what to do with them. They only contain filename without absolute path.

import React, { useCallback } from 'react';
import { useDropzone } from 'react-dropzone'

const App = () => {
  const maxSize = 1048576;

  const onDrop = useCallback(acceptedFiles => {
    console.log(acceptedFiles);
  }, []);

  const { isDragActive, getRootProps, getInputProps, isDragReject, acceptedFiles, rejectedFiles } = useDropzone({
    onDrop,
    accept: 'image/png, image/jpeg',
    minSize: 0,
    maxSize,
  });

  const isFileTooLarge = rejectedFiles.length > 0 && rejectedFiles[0].size > maxSize;
  
  return (
    <div className="container text-center mt-5">
      <div {...getRootProps()}>
        <input {...getInputProps()} />
        {!isDragActive && 'Click here or drop a file to upload!'}
        {isDragActive && !isDragReject && "Drop it like it's hot!"}
        {isDragReject && "File type not accepted, sorry!"}
        {isFileTooLarge && (
          <div className="text-danger mt-2">
            File is too large.
          </div>
        )}
      </div>
    </div>
  );
};

export default App;
1 Like

Hey @avn3r, glad to see another person jumping in on the components train!

One thing I want to highlight is that weā€™re planning on overhauling the file_uploader function in base Streamlit by Q4 this year, so uploading multiple files might be supported before you know it. @karriebear can hopefully speak to this a bit more.

1 Like

Thatā€™s right! Weā€™re actively working on the file uploader enhancements which will include support for multiple files. This is targeted for beginning of Q4.

Regarding your custom component, react-dropzone returns a File object.

  • To view images without uploading anywhere, rather than using st.image, it would be easier to display them within your react component. react-dropzone actually had this functionality in previous versions but removed it. Luckily theyā€™ve provided an example implementation here
  • To upload files to S3, youā€™ll need to send the data to S3. AWS has a SDK available to do this here.

Hope this helps!

1 Like

Thanks I got it working with another library react-dropzone-uploader. Can upload up to 500 files without issue the main issue is still communicating back with Streamlit. I would like to just pass the files before upload to streamlit so that I can handle the S3 upload as a background process.

I want to get all the files pass them to st.image and in the background upload them. Right now I been having to upload them directly to S3 and the show them with st.image but that makes the UX slow cuz I have to wait until they upload all the files which can be 1k+ filesā€¦

Any suggestions welcomed. I a noob at reactā€¦ But will try my best. Also can streamlit components work with hooks or functions or only Class Components?

1 Like

Hey @avn3r. Sure you can use post-hooks React in Streamlit Custom Components :slight_smile: I and I think @okld too mostly used React hooks in the beta without any issues, check my D3 React hooks experiments or Synodeā€™s Ace editor for random examples.


Going further, though there are no public examples (yet), you are not limited to React to build Custom Components ! Streamlit Custom Components runs arbitrary HTML/CSS/Javascript in an iframe, as long as you subscribe to specific Streamlit JS events to trigger the rendering and send back data from the component to the Python script. You can find which JS events you need to subscribe to inside the template-reactless folder of the project, in frontend/src/index.tsx.

The React template does all of the subscribing for you inside the withStreamlitConnection component in frontend/src/streamlit/StreamlitReact.tsx for the React template. If you are more of a Angular, Ember, Vue, Svelte, etcā€¦user, youā€™re free to replicate this event subscription behavior in your preferred JS stack :wink:


Iā€™d follow @karriebearā€™s suggestion,

you should have access to your image files in an array before the handleSubmit (I did not check your code so Iā€™m making an assumption thereā€¦), you can display them under your file uploader with one of those React image browser. They are better suited for displaying multiple images rather than a sequence of st.image calls, at least you can control the disposition of the images in HTML/CSS, as a grid of small images or in a carousel. It will be harder to scroll 500+ st.image calls.

Hope you get to the end of it !

Thanks @andfanilo. This helps.

Actually st.image is the only widget that allows grid like behavior. I can control how many rows how many columns and I use left and right buttons to display next set of image, so displaying on the streamlit site is pretty decent, only thing I canā€™t control is the height of the images.

I already got it to work by using react component to directly uploading to S3 and the using st.image. However this requires upload first then display which is a bit too slow. I would prefer direct interaction with Streamlit so that I can use st.image before uploading and then just have a python background process that uploads while I do stuff with the images.

So the main question or place Iā€™m stuck is how to return the list of images (maybe as byte List) in a format that I can read from st.image. In react I can handle an image and I have acceptedFiles but I need something to make them into image bytes list of acceptedFiles or something like that, that st.image can handle.

Thanks for the help.

500 files in one go? You got me curious! :slight_smile: