Search code examples
arraysreactjsamazon-web-servicesamazon-s3aws-amplify

Loop through array of multiple images to upload individually to AWS s3 ReactJS


Im building an upload page that is supposed to take multiple images and upload them to aws s3 using AWS amplify storage.

Im having a few issues

  • I need to encode all images to base64 format before uploading.

  • It only allows one image to be uploaded at a time, so I need to loop through the array of files and upload each one individually.

  • i am unable to get the name of the file by using file.name.

How should I loop through the array of imagefiles encode them in base64, and upload each one individually?

The below code works for uploading one image at a time, but the file name is undefined.

 const file = useRef(null);

 function handleFileChange(event) {

        file.current = btoa(event.target.files); 

   console.log(event.target.files) ///// length: 7
                           /// 0: File {name: "921 crest road (19 of 91).jpg", lastModified: 1579836842179, lastModifiedDate: Thu Jan 23 2020 22:34:02 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 13998488, …}
                          /// 1: File {name: "921 crest road (20 of 91).jpg", lastModified: 1579836859659, lastModifiedDate: Thu Jan 23 2020 22:34:19 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 14433420, …}
                         /// 2: File {name: "921 crest road (21 of 91).jpg", lastModified: 1579836860868, lastModifiedDate: Thu Jan 23 2020 22:34:20 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 14524865, …}
                        /// 3: File {name: "921 crest road (22 of 91).jpg", lastModified: 1579836861497, lastModifiedDate: Thu Jan 23 2020 22:34:21 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 13995939, …}
                       /// 4: File {name: "921 crest road (23 of 91).jpg", lastModified: 1579836884687, lastModifiedDate: Thu Jan 23 2020 22:34:44 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 13982365, …}
                      /// 5: File {name: "921 crest road (24 of 91).jpg", lastModified: 1579836885360, lastModifiedDate: Thu Jan 23 2020 22:34:45 GMT-0500 (Eastern Standard Time), webkitRelativePath: "", size: 14288288, …}
                     /// 6: File {name: "921 crest road (25 of 91).jpg", lastModified: 1579836886846, lastMod

            console.log(file.current) //// undefined
  }


    async function handleSubmit(event) {
        event.preventDefault();
        setShowLoading(true);

    try {

            console.log(file.name) ///undefined

            const filename = `${job.jobId}---${file.name}`;

            const stored = await Storage.put(filename, file.current, {
                contentType: file.type
        });

         } catch (e) {
            alert(e);
            console.log(e.message)
            }
         setShowLoading(false);
    }



  var centerText = {textAlign: "center"}

  return(
      <IonPage>
      <IonHeader>
        <IonToolbar>
      <IonButtons slot="start">
          <IonBackButton/>
        </IonButtons>
          <IonTitle>Upload Images</IonTitle>
        </IonToolbar>
      </IonHeader>  
      <IonContent className="ion-padding">

      <form onSubmit={handleSubmit}>

      <input multiple="true" type="file" onChange={(e) => handleFileChange(e)}></input>
      <IonButton  expand="block" color="primary" strong="true" size="default" type="submit" >Upload</IonButton>

      </form>

    </IonContent>
    </IonPage>
    );


  }


export default withRouter(JobInfo);

Updated Code --still not working!

const file = useRef(null);

  function extractInfo(file) {
        console.log(file)
     return { 
      name: file.name,
      type: file.type,
      content: btoa(file.content),
   };

}

    async function handleFileChange(event) {
    file.current = event.target.files;

  }

    async function handleSubmit(event) {
   const result = await Promise.all(
      file.current.files
          .map(extractInfo)
          .map(uploadFile)
   );
        console.log(file.files)
    }


    async function uploadFile(file) {

         setShowLoading(true);

        try {

  const filename = `${job.jobId}-${file.name}`;

  const stored = await Storage.put(filename, file.current, {
    contentType: file.type
  });


  } catch (e) {
    alert(e);
    console.log(e.message)

  }
 setShowLoading(false);
}
  var centerText = {textAlign: "center"}

  return(
      <IonPage>
      <IonHeader>
        <IonToolbar>
      <IonButtons slot="start">
          <IonBackButton/>
        </IonButtons>
          <IonTitle>Upload Images</IonTitle>
        </IonToolbar>
      </IonHeader>  
      <IonContent className="ion-padding">

      <form onSubmit={handleSubmit}>

       <input multiple type="file" ref={file} onChange={(e) => handleFileChange(e)}></input></input>
      <IonButton  expand="block" color="primary" strong="true" size="default" type="submit" >Upload</IonButton>

      </form>

    </IonContent>
    </IonPage>
    );


  }


export default withRouter(JobInfo);

Solution

  • You forgot to use ref in your input element. Using Promise.all to chain all the promises.

    So your problem can be solved by something like this:

    const { useState, useRef } = React;
    
    function Uploader() {
       const file = useRef({});
       
       function readContent(file) {
          return new Promise((accept, reject) => {
             const reader = new FileReader();
             reader.readAsDataURL(file);
             reader.onload = () => accept({
                name: file.name,
                type: file.type,
                content: reader.result
             });
             reader.onerror = () => reject();
          });
       }
      
       function upload(file) { // fake upload
       
          return new Promise(accept => {
             setTimeout(() => accept(file), 1000);
          });
       }
       
       function onSubmit(event) {
          event.preventDefault();
          
          const filesAsArray = [...file.current.files];
          const fileInfo = Promise.all(filesAsArray.map(readContent))
              .then(files => Promise.all(files.map(upload)))
              .then(console.log);
          
          return false;
       }
       
       return (
         <div>
           <form onSubmit={onSubmit}>
             <input ref={file} type="file" multiple={true} />
             <input type="submit" value="Upload" />
           </form>
         </div>
       );
    }
    
    ReactDOM.render(<Uploader />, document.getElementById("root"));
    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    
    <div id="root"></div>