[FIXED] Ionic 6 – Capacitor Filesystem – copy SQLite files from ZIP

Issue

Hello everyone and thanks for taking time to read and answer.

DESCRIPTION

I am using Ionic 6 with Angular 14 and capacitor. I need to copy the content of an ZIP file uploaded by the user (it contains .sqlite files), and store it inside the Documents folder. For that, I’m using JSZip library.

  public async unzipFolder(file: File): Promise<string[]> {
    const zip: JSZip = await JSZip.loadAsync(file);
    const promises: Promise<string>[] = [];

    zip.forEach((relativePath: string, zipFile: JSZipObject) =>
        promises.push(this.unzipFile(zipFile));
    );
    return await Promise.all(promises);
  }

  public async unzipFile(compressedFile: JSZipObject): Promise<string> {
    return await compressedFile.async('string').then((data: string) => {
     this.fileOpsService.write(compressedFile.name, data)
    });
  }

Everything works fine here. Problems comes when I try to store it in Documents folder using Filesystem plugin (capacitor).

  public async write(
    path: string,
    data: string,
    directory: Directory = Directory.Documents
  ): Promise<WriteFileResult> {
    return await Filesystem.writeFile({
      path: path,
      directory: directory,
      data: data,
      recursive: false,
    });
  }

XCode’s terminal throws this error:

{"errorMessage":"Unable to save file"}

I tried File plugin (awasome-cordova-file) but it does not work.

  public async write(
    path: string,
    data: ArrayBuffer,
    directory: Directory = Directory.Documents
  ): Promise<any> {
    return await File.writeFile(path, '', data, {replace: true})
  }

It throws the next error:

[DocumentManager] Failed to associate thumbnails for picked URL file:///private/var/mobile/Containers/Shared/AppGroup/XXX/File%20Provider%20Storage/TEST_BATCH2.zip with the Inbox copy file:///private/var/mobile/Containers/Data/Application/XXX/tmp/io.ionic.starter.ivan-Inbox/TEST_BATCH2.zip: Error Domain=QLThumbnailErrorDomain Code=102 "(null)" UserInfo={NSUnderlyingError=0x282532cd0 {Error Domain=GSLibraryErrorDomain Code=3 "Generation not found" UserInfo={NSDescription=Generation not found}}}

TESTED SOLUTIONS

  1. I tried to read the ZIP files as ArrayBuffer or Blob, but the
    Filesystem plugin can only use string data, so it didn’t work.
  2. I also try to change the encoding value of Filesystem to UTF-8,
    UTF-16 and ASCII but it didn’t work. The default value is Base64
    encoding.
  3. I try to use the cordoba File plugin. In previous app versions this was working
    fine, so I try it (when the app used cordoba instead capacitor). Didn’t work.

THEORIES

  1. I’m not sure about what is happening here, but seems than could be something related
    with encoding. Filesystem is not designed 4 copy .sqlite files I suppose.
  2. Maybe something related with relative paths, but I don’t think so.
  3. Some Apple sh*t, u know…

Notes

  1. I just need it to work in iOS and Electron platforms. Browser could be a plus, but is not required.
  2. I know that an API/REST approach could be better, but it’s not posible for this app. We need to store the data localy.

Solution

Ok. After a few days, I finally found a solution. Here is it, if it can help anyone…

I changed the way of read from ZIP file by replacing ‘string’ with ‘blob’ as you can see in the following snippet:

  public async unzipFile(compressedFile: JSZipObject): Promise<void> {
    return await compressedFile.async('blob').then(async (data: Blob) => {
      const filename: string = compressedFile.name
        .replace('.sqlite', 'SQLite.db');
      const base64 = await this.convertBlobToBase64(data);
      this.fileOpsService.write(filename, base64);
    });
  }

I create a convertBlobToBase64 helper function that looks like this:

  private convertBlobToBase64 = (blob: Blob) => new Promise((resolve, reject) => {
    const reader: FileReader = new FileReader();
    reader.onerror = reject;
    reader.onload = () => {
      resolve(reader.result);
    }
    reader.readAsDataURL(blob);
  })

And it works! Note that I replaced the filename because I need it in that format to read it with another plugin =)

Answered By – N0M4D

Answer Checked By – Timothy Miller (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published