-1

i am using fileReader for uploading images, when images are done uploading i want to send them to server.

my problem is that when there are more than 1 image it sends multiple requests, because i have for loop. how can i make it work?

for (let file of selectedFiles) {
    const fileName = file.name;
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    reader.onload = () => {
      const image = reader.result as any;
      var base64 = btoa(
        new Uint8Array(image)
          .reduce((data, byte) => data + String.fromCharCode(byte), '')
      );
      imageArray.push({ fileName: fileName, content: base64 });
      console.log(imageArray);
      this.service.create(this.finalData).subscribe({
        next: data => {
          console.log(data);
          this.openSnackBar("Success", 'x');
        },
        error: error => {
          console.error('There was an error!', error);
        }
      })
    }
  }

1 Answers1

4

If you're dealing with multiple asynchronous things at once, then you will save yourself a lot of effort by using promises.

Start by writing a function which takes a file and returns a promise that resolves to the data you want.

I'll define FileData here to make it clear how things work out later.

type FileData = {
    fileName: string;
    content: string; // base64 encoded
};

const getFileData = (file) => {
    const fileName = file.name;
    
    const promise = new Promise(
        (res, rej) => {
            const reader = new FileReader();
            reader.readAsArrayBuffer(file);
            reader.onload = () => {
                const image = reader.result as any;
                var base64 = btoa(
                    new Uint8Array(image)
                      .reduce((data, byte) => data + String.fromCharCode(byte), '')
                  );
                const data: FileData = { fileName: fileName, content: base64 };
                res(data)
            };
    });

    return promise;
};

Then do your loop but use the function to get your data and gather the results into an array.

const promises = [];
for (let file of selectedFiles) {
    promises.push(getFileData(promises));
};

Then use Promise.all to run another function once all the promises are resolved and you have the data.

Promise.all(promises).then((dataArray: FileData[]) => {

    // Now work with dataArray and upload all the data in the array in one go

});

That said. I'd look at changing the server to support a standard multipart MIME formatted request and then upload the data using FormData instead of converting everything to base64 myself.

const data = new FormData(document.getElementById('myForm');
const responsePromise = fetch('/url', { method: "POST", body: data });

… is a lot less effort than the above! And sends all the same data (just in a different format).

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335