2

I'm trying to download multiple files using react-native-fs library and show the progress of the download to the user. For a single file download It can be use as the following code.

let fileSize = null;
let downloadedSize = null;
let progress = null;

RNFS.downloadFile({
  fromUrl: 'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_480_1_5MG.mp4',
  toFile: `${RNFS.DocumentDirectoryPath}/download.mp4`,
  begin: ({jobId, contentLength}) => { fileSize = contentLength; },
  progress: ({jobId, bytesWritten, contentLength}) => {
    downloadedSize = bytesWritten;
    console.log("progress", downloadedSize / fileSize);
  },
});

For multiple file downloads I'm trying to use the same function with a for of loop. But was not able to get the progress correctly. The progress values are displayed like this

0,10,20,30,40,50,60,70,80,90,50,60,70,80,90,80,90,100

The values are going back and forth. Following is the my code

const files = [
  'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_480_1_5MG.mp4',
  'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_640_3MG.mp4',
  'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1280_10MG.mp4',
  'https://file-examples-com.github.io/uploads/2017/04/file_example_MP4_1920_18MG.mp4',
];

const promises = [];
let jobs = [];
let index = 1;

for (const file of files) {
  const promise = RNFS.downloadFile({
    fromUrl: file,
    toFile: `${RNFS.DocumentDirectoryPath}/download_${index}.mp4`,
    begin: ({ jobId, contentLength }) => {
      jobs.push({ jobId, contentLength, progress: 0 })
    },
    progress: ({ jobId, bytesWritten, contentLength }) => {
      jobs = jobs.map((job, index) => {
        if (jobId === job.jobId) {
          jobs[index].progress = bytesWritten;
        }
        return job;
      });
      const totalDownloadSize = jobs.reduce((total, current) => total + current.contentLength, 0);
      const currentDownloadSize = jobs.reduce((total, current) => total + current.progress, 0);
      console.log("progress", currentDownloadSize / totalDownloadSize);
    },
  });
  promises.push(promise);
  index++;
}
Promise.all(promises);

Is there anyway I can get the progress correctly?

camille
  • 278
  • 1
  • 2
  • 20
  • 2
    When is `begin` called? – Jonas Wilms Dec 30 '21 at 17:38
  • @JonasWilms begin is called before downloading the file. But when it comes to multiple files some files' progress function will call before another files' begin function – camille Dec 30 '21 at 17:46
  • 1
    Then I'd pre initialize the array with some speculative large `contentLength`, and update it once begin is called. – Jonas Wilms Dec 30 '21 at 17:48
  • 1
    Or alternatively calculate `percentage += (transmitted / size) / files.length` (which is a slightly different indication, though might he what you're looking for) – Jonas Wilms Dec 30 '21 at 17:50
  • 1
    Aside: instead of `jobs[index]` you should just refer to `job` inside that `map` function. And you shouldn't actually use `map` if you don't plan to return different objects, use `for … of` loops to mutate objects in arrays. – Bergi Dec 30 '21 at 17:55
  • 1
    Does `downloadFile` has builtin throttling? – Bergi Dec 30 '21 at 17:56
  • @JonasWilms even though I do that (transmitted/size) will not be continuous right? It can go back & forth – camille Dec 30 '21 at 18:01
  • @Bergi actually its a library function to download a single file. I didn't see it has built in throttling in the source code – camille Dec 30 '21 at 18:01

1 Answers1

2

I would measure

(currentDownloadSize / totalDownloadSize) * (jobs.length / files.length)

to factor in the not-yet-started jobs.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375