Cache images not Uploading to server

Hi

I’ve created an application that takes photos and then needs to send them up to a server.

The problem that I’m having is that when doing the upload only some of the images are sent to the server.

It seems that images that come from the file location work fine.

file:///data/user/0/io.ionic.starter/files/filename.jpg

and then the images that come from the cache don’t upload at all.

file:///data/user/0/io.ionic.starter/cache/filename.jpeg

First question, what would cause the same code to save the images in different locations? Could this be a product of using local storage?

I’ve pretty much followed the Your First Ionic App: React tutorial to create the app. I did make some changes as I didn’t require a gallery.

I’ve included the entire hook file that I’m using below.

Thanks in advance.

import { useState, useEffect } from "react";
import { useCamera } from "@ionic/react-hooks/camera";
import { useFilesystem, base64FromPath } from "@ionic/react-hooks/filesystem";
import { useStorage } from "@ionic/react-hooks/storage";
import { isPlatform } from "@ionic/react";
import {
  CameraResultType,
  CameraSource,
  CameraPhoto,
  Capacitor,
  FilesystemDirectory,
} from "@capacitor/core";

export interface Photo {
  filepath: string;
  webviewPath?: string;
  base64?: string;
}

export interface GalleryPhoto {
  webviewPath?: string;
  base64?: string;
}

const PHOTO_STORAGE = "photos";

export function usePhoto() {
  const { getPhoto } = useCamera();
  const [photos, setPhotos] = useState<Photo[]>([]);
  const { deleteFile, getUri, readFile, writeFile } = useFilesystem();
  const { get } = useStorage();

  useEffect(() => {
    /* On mobile, we can directly point to each photo file on the Filesystem and 
    display them automatically. On the web, however, we must read each image from 
    the Filesystem into base64 format, using a new base64 property on the Photo 
    object. This is because the Filesystem API uses IndexedDB under the hood. */
    const loadSaved = async () => {
      const photosString = await get(PHOTO_STORAGE);
      const photosInStorage = (photosString
        ? JSON.parse(photosString)
        : []) as Photo[];

      // Running on the web
      if (!isPlatform("hybrid")) {
        for (let photo of photosInStorage) {
          const file = await readFile({
            path: photo.filepath,
            directory: FilesystemDirectory.Data,
          });
          photo.base64 = `data:image/jpeg;base64,${file.data}`;
        }
      }
      setPhotos(photosInStorage);
    };
    loadSaved();
  }, [get, readFile]);

  const takePhoto = async (fileNamePrefix: string) => {
    const cameraPhoto = await getPhoto({
      resultType: CameraResultType.Uri,
      source: CameraSource.Camera,
      quality: 65,
      width: 700,
      height: 900,
    });

    const fileName = `${fileNamePrefix}-${new Date().getTime()}.jpg`;
    // return await savePhoto(cameraPhoto, fileName);
    const savedFileImage = await savePhoto(cameraPhoto, fileName);
    // console.log(savedFileImage.base64);
    // console.log(savedFileImage.filepath);
    // console.log(savedFileImage.webviewPath);
    const newPhotos: Photo[] = [savedFileImage, ...photos];

    setPhotos(newPhotos);
  };

  const savePhoto = async (photo: CameraPhoto, fileName: string) => {
    let base64Data: string;

    // Check if we are running on Capacitor or Cordova. (On mobile device)
    if (isPlatform("hybrid")) {
      const file = await readFile({
        path: photo.path!,
      });
      base64Data = file.data;
    } else {
      base64Data = await base64FromPath(photo.webPath!);
    }

    await writeFile({
      path: fileName,
      data: base64Data,
      directory: FilesystemDirectory.Data,
    });
    return getPhotoFile(photo, fileName, base64Data);
  };

  const getPhotoFile = async (
    cameraPhoto: CameraPhoto,
    fileName: string,
    base64: string
  ): Promise<Photo> => {
    if (isPlatform("hybrid")) {
      // Get the new, complete filepath of the photo saved on filesystem
      const fileUri = await getUri({
        directory: FilesystemDirectory.Data,
        path: fileName,
      });

      // Display the new image by rewriting the 'file://' path to HTTP
      // Details: https://ionicframework.com/docs/building/webview#file-protocol
      return {
        filepath: fileUri.uri,
        webviewPath: Capacitor.convertFileSrc(fileUri.uri),
        base64,
      };
    }

    // Use webPath to display the new image instead of base64 since it's already loaded into memory
    return {
      filepath: fileName,
      webviewPath: cameraPhoto.webPath,
      base64,
    };
  };

  const deletePhoto = async (photo: Photo) => {
    // Remove this photo from the Photos reference data array
    const newPhotos = photos.filter((p) => p.filepath !== photo.filepath);

    // delete photo from filesystem
    const filename = photo.filepath.substr(photo.filepath.lastIndexOf("/") + 1);
    await deleteFile({
      path: filename,
      directory: FilesystemDirectory.Data,
    });
    setPhotos(newPhotos);
  };

  const selectPhoto = async (): Promise<void> => {
    const galleryPhoto = await getPhoto({
      resultType: CameraResultType.Uri,
      source: CameraSource.Photos,
      quality: 100,
    });
    const photo = {
      filepath: galleryPhoto.path ?? "",
      webviewPath: galleryPhoto.webPath,
      base64: galleryPhoto.base64String,
    };
    const newPhotos: Photo[] = [photo, ...photos];
    setPhotos(newPhotos);
  };

  return { photos, takePhoto, selectPhoto, deletePhoto };
}

Right, so it turns out that the solution was pretty simple in the end.

The answer to my first question is easy.
When using the getPhoto() function, if the photo is being taken via the camera, the URI returned points to file:///data/user/0/io.ionic.starter/files, when the image is from the phone’s gallery the path points to the file:///data/user/0/io.ionic.starter/cache.

The answer to my real question of why it’s not uploading was because, when I selected the image from the Gallery, I didn’t get the base64 string from the image before setting it to my state.

const selectPhoto = async (): Promise<void> => {
    const galleryPhoto = await getPhoto({
      resultType: CameraResultType.Uri,
      source: CameraSource.Photos,
      quality: 100,
    });

    const base64Data = await base64FromPath(galleryPhoto.webPath!);  // <!--- Here.

    const photo = {
      filepath: galleryPhoto.path ?? "",
      webviewPath: galleryPhoto.webPath,
      base64: base64Data,  // <!--- and here.
    };

    const newPhotos: Photo[] = [photo, ...photos];

    setPhotos(newPhotos);
  };
1 Like