How to handle multiple images to download using Next.js?

238 views Asked by At

I have a route called /photos, which supports pagination through a 'page' query parameter and allows me to specify the number of photos per page using 'pageSize'. It returns an array of photos and the total count of photos available.

This is my api route
/photos?cameraId=580&start=2023-10-29&end=2023-10-30&beginTime=18:00&endTime=19:00&Page=1&PageSize=10
This is my return object
{
    "photos": [
        {  "urlPreview": "https://example.com/random-preview-url"  }
        ...
    ],
    "total": 239
}

Now, I need to implement a button on my page that, when clicked, will start downloading these photos. The total number of photos is approximately 12K, which obviously takes some time and processing.

I've been advised to use localStorage to save the photos incrementally and then zip them all for the user to download.

However, I have no idea how to implement this as I've never done anything like this before. Can someone please guide me on how to proceed?

I was trying to download using my downloadMassivePhotos function like this

import JSZip from 'jszip';
import { saveAs } from 'file-saver';

export const downloadMassivePhotos = async (
  cameraId,
  start,
  end,
  beginTime,
  endTime,
) => {
  const zip = new JSZip();
  const maxConcurrentDownloads = 20;
  let activeDownloads = 0;

  try {
    const { photos } = await fetchAPI({
      path: 'photos',
      query: { cameraId, start, end, beginTime, endTime },
    });

    for (const photo of photos) {
      while (activeDownloads >= maxConcurrentDownloads) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
      }

      activeDownloads++;

      fetchAndZipPhoto(photo, zip)
        .catch((error) => {
          console.error(`Error downloading photo ${photo.id}:`, error);
        })
        .finally(() => {
          activeDownloads--;
        });
    }

    while (activeDownloads > 0) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
    }

    const content = await zip.generateAsync({ type: 'blob' });
    saveAs(content, 'photos.zip');
  } catch (error) {
    console.error(error);
  }
};

const fetchAndZipPhoto = async (photo, zip) => {
  const { urlImage, id } = photo;
  const response = await fetch(urlImage);
  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }
  const blob = await response.blob();
  zip.file(`${id}.jpg`, blob, { binary: true });
};

And this is my fetch method

export const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_HOST,
});

export const fetchAPI = async <T>({ path, query, context }: FetchAPI) => {
  const session = await getSession(context);

  const { data, status } = await api.get<T>(`/${path}`, {
    headers: { Authorization: `Bearer ${session?.token}` },
    params: query,
  });

  if (!data && status !== 200) {
    throw new Error(`API call to ${path} failed.`);
  }

  return data;
};
0

There are 0 answers