Ionic 5/Cordova 9 Social Media Post With File Attachment

Hello, I need some help.

Environment:

Ionic:

   Ionic CLI                     : 6.3.0
   Ionic Framework               : @ionic/angular 5.2.1
   @angular-devkit/build-angular : 0.803.27
   @angular-devkit/schematics    : 8.3.27
   @angular/cli                  : 8.3.27
   @ionic/angular-toolkit        : 2.2.0

Cordova:

   Cordova CLI       : 9.0.0 (cordova-lib@9.0.1)
   Cordova Platforms : android 8.1.0
   Cordova Plugins   : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 4.2.1, (and 9 other plugins)

Utility:

   cordova-res                          : not installed
   native-run (update available: 1.0.0) : 0.3.0

System:

   Android SDK Tools : 26.1.1
   NodeJS            : v10.16.0
   npm               : 6.9.0
   OS                : Linux 5.3

Basic Goals:
Just like the title says, I am trying to create a very basic social media page, where anyone can type text into a box, attach file(s), and click post. Similar to every common social media platform like facebook, instagram, ect.
The attached files (pictures and videos only), will display on the list of posts when a user scrolls to view them, similar to other social media platforms.

I can handle the fetching of posts and showing them on the page. My problem exists with the file uploader. Nothing I have tried works. I’ll outline (to the best of my abilities and memory) what I tried. I say that because I have made many code changes since in an attempt to remedy the problems I have created.

First method:
I followed this tutorial basically just copied and pasted all the code. It worked OK for videos, but as soon as I tried to modify Cordova’s camera plugin options such as MediaType and some other options, it started falling apart. I started to console.log() everything to see what was going on (because I’m learning) and figured out that the file was there I just needed to mess with the code to get the path to it. So I went into the cordova file plugin. I started reading through these docs, as well as the ionic native docs (which I know it’s v3 but god forbid there is decent v5 docs…)… Eventually I started messing around with different functions like resolveDirectoryUrl, resolveNativePath, eventually readAsDataURL.

resolveDirectoryUrl gave me a few different errors, like error code 5. So I’m like okay let me research that for a solution, I would find a potential solution and implement it. Error code 2, research more… Error code 1000, research more… Error code 13… At that point I was ready to quit so I did. I tried swapping different substrings in the path like ‘file:///’ and ‘content://’ to try to get it to allow access. So i was like alright I see how it’s going to be.
The next thing I figured I could try was to get a base64 string for the files and convert it to a file on server side. So I started using resolveNativePath, and readAsDataURL which seemed to work okay. Then I got a permissions error which is where I’m currently at:
Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/ from pid=7725, uid=10384 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
So like always I hit google with it. I modified the config.xml based on what like 2 different pages said, modified the android manifest, requested permissions in the code.
This returns true:

this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE).then(
            result => console.log('Has permission?',result.hasPermission),
            err => this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
          );

And I just didn’t know what to do at this point. The next thing I will be trying until I can find a plausible solution here is going to be to use the default HTML5 file input field. I have no idea how well it’s going to work but it’s honestly my last resort solution. Here is my current .ts file contents for the function where the error pops up:

selectMedia() {
    const options: CameraOptions = {
      mediaType: this.camera.MediaType.ALLMEDIA,
      sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
      destinationType : this.camera.DestinationType.FILE_URI
    }

    this.camera.getPicture(options)
      .then( async (mediaURl) => {
        if (mediaURl) {
          this.showLoader();
          this.uploadedVideo = null;

          var filename = mediaURl.substr(mediaURl.lastIndexOf('/') + 1);
          var dirpath = mediaURl.substr(0, mediaURl.lastIndexOf('/') + 1);

          // dirpath = dirpath.includes("file:///") ? dirpath : "file:///" + dirpath;

          this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE).then(
            result => console.log('Has permission?',result.hasPermission),
            err => this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
          );

          this.androidPermissions.requestPermissions([this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE]);

          try {
            console.log(0);
            var nativeDirUrl = await this.filePath.resolveNativePath(dirpath);
            var dirUrl = await this.file.readAsDataURL(nativeDirUrl, filename);
            // var dirUrl = await this.file.resolveDirectoryUrl(nativeDirUrl);
            console.log(1);
            console.log(dirUrl);
            // var retrievedFile = await this.file.getFile(dirUrl, filename, {});
            console.log(2);
          } catch(err) {
            //this.dismissLoader();
            console.log(err);
            return this.presentAlert("Error","Something went wrong.  ");
          }

          // retrievedFile.file( data => {
          //     //this.dismissLoader();
          //     if (data.size > MAX_FILE_SIZE) return this.presentAlert("Error", "You cannot upload more than 5mb.");
          //     // if (data.type !== ALLOWED_MIME_TYPES) return this.presentAlert("Error", "Incorrect file type.");
          //
          //     this.selectedVideo = retrievedFile.nativeURL;
          // });
        }
      },
      (err) => {
        console.log(err);
      });
  }

and the .html:

<ion-card>
    <ion-card-header id="postHeader" >Create Post</ion-card-header>
    <ion-item id="postBox">
      <ion-textarea id="posttextarea" rows="1" cols="20" placeholder="Enter any notes here..."></ion-textarea>
    </ion-item>
    <ion-icon name="attach-outline" (click)="selectMedia()"></ion-icon>
    <ion-icon name="happy-outline"></ion-icon>
    <div class="video-section" *ngIf="selectedVideo">

      <video controls [src]="selectedVideo"></video>

      <div class="button-options" *ngIf="!uploadedVideo && !isUploading">
        <button ion-button clear (click)="cancelSelection()">
          <ion-icon name="close-circle" color="danger"></ion-icon>
        </button>
        <button ion-button clear (click)="uploadVideo()">
          <ion-icon name="checkmark-circle" color="secondary"></ion-icon>
        </button>
      </div>

      <div *ngIf="isUploading">
        <div class="uploading">
          <p><ion-spinner name="bubbles"></ion-spinner></p>
          <p>Uploading - {{ uploadPercent }}% Complete</p>
        </div>
        <div class="button-options">
          <button ion-button clear (click)="cancelUpload()">
            <ion-icon name="close-circle" color="danger"></ion-icon>
          </button>
        </div>
      </div>

      <div class="button-options" *ngIf="uploadedVideo">
        <button ion-button clear (click)="cancelSelection()">
          Start Over
        </button>
      </div>

    </div>
    <ion-button id="publishButton" size="small" color="medium" (click)="createNewPost()">Publish</ion-button>
  </ion-card>

Honestly a reliable set of docs/tutorial would be great right now if anyone has one that I haven’t seen for Ionic 5, angular, and cordova 9. It doesn’t seem like there is enough popularity with it right now to really have enough tutorials so I feel like I’m having to read like a frankenstein version of different documentation trying to make it work. If not, a simple walkthrough or code example of the best way to do this would be amazing. I’m a person who can see it done and thrive from there, but paving my own path to the solution is not very lucky most days. Unfortunately it’s what I’m forced to do with this tool often times. Sorry for the long post just wanted to provide as many details as possible to help get an answer. Thank you in advance and please let me know if there is anything I need to add/subtract.

Here is my working solution (Cordova 9, Ionic 5, Angular 10, cordova-android 9.0.0) for reading files with Android:

import { FileChooser } from '@ionic-native/file-chooser/ngx';
import { FilePath } from '@ionic-native/file-path/ngx';
import { File, FileEntry } from '@ionic-native/file/ngx';

  public constructor(
    private file: File,
    private fileChooser: FileChooser,
    private filePath: FilePath
  ) {}

  public async getMp3(callback: Function): Promise<void> {
        const fileURI: string = await this.fileChooser.open();
        const filePathUrl: string = await this.filePath.resolveNativePath(fileURI);
        const fileName: string = filePathUrl.substring(filePathUrl.lastIndexOf('/') + 1);
        const fileEntry: FileEntry = await this.file.resolveLocalFilesystemUrl(fileURI) as FileEntry;
        const reader: FileReader = new FileReader();

        let fileReadResult: string | ArrayBuffer;
        let mp3File: any;

        fileEntry.file((file: any): void => {
          mp3File = file;
          reader.readAsDataURL(mp3File);
        });

        reader.onloadend = (): void => {
          fileReadResult = reader.result;
          callback({
            data: { base64: fileReadResult as string },
            fileName
          });
        };
  }

Now if I can get it to work for iOS…