File.writeFile() refuses to create binary file correctly (png image)


#1

Hi,

In summary,
File.writeFile() creates a PNG file of 0 bytes when trying to write a Blob made from base64 data.

I hope this is the right section for this. In my application, i am trying to create a file that consists of base64 data stored in the db. The rendered equivalent of the data is a small anti-aliased graph curve in black on a transparent background (never more that 300 x 320 pixels) that has previously been created and stored from a canvas element. I have independently verified that the stored base64 data is indeed correct by rendering it at one of various base64 encoders/decoders available online.

Output from “Ionic Info”

--------------------------------
Your system information:

Cordova CLI: 6.3.1
Gulp version:  CLI version 3.9.1
Gulp local:
Ionic Framework Version: 2.0.0-rc.2
Ionic CLI Version: 2.1.1
Ionic App Lib Version: 2.1.1
Ionic App Scripts Version: 0.0.39
OS:
Node Version: v6.7.0
--------------------------------

The development platform is Windows 10, and i’ve been testing directly on a Samsung Galaxy S7 and S4 so far.

I know that the base64 data has to be converted into binary data (as a Blob) first, as File does not yet support writing base64 directly in to an image file. I found various techniques with which to do this, and the code which seems to suit my needs the most (and reflects a similar way I would have done it in java is illustrated below):

Main code from constructor:

this.platform.ready().then(() => {
      this.graphDataService.getDataItem(this.job.id).then((data) =>{
        console.log("getpic:");

        let imgWithMeta = data.split(",") 
        // base64 data
        let imgData = imgWithMeta[1].trim();
        // content type
        let imgType = imgWithMeta[0].trim().split(";")[0].split(":")[1];
        
        console.log("imgData:",imgData);
        console.log("imgMeta:",imgType);
        console.log("aftergetpic:");

        // this.fs is correctly set to cordova.file.externalDataDirectory
        let folderpath = this.fs;
        let filename = "dotd_test.png";

        File.resolveLocalFilesystemUrl(this.fs).then( (dirEntry) => {
            console.log("resolved dir with:", dirEntry);
            this.savebase64AsImageFile(dirEntry.nativeURL,filename,imgData,imgType);
        });
      });

    });

Helper to convert base64 to Blob:

// convert base64 to Blob
b64toBlob(b64Data, contentType, sliceSize) {

          //console.log("data packet:",b64Data);
          //console.log("content type:",contentType);
          //console.log("slice size:",sliceSize);

          let byteCharacters = atob(b64Data);
          
          let byteArrays = [];

          for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
              let slice = byteCharacters.slice(offset, offset + sliceSize);

              let byteNumbers = new Array(slice.length);
              for (let i = 0; i < slice.length; i++) {
                  byteNumbers[i] = slice.charCodeAt(i);
              }

              let byteArray = new Uint8Array(byteNumbers);

              byteArrays.push(byteArray);

          }
          

        console.log("size of bytearray before blobbing:", byteArrays.length);
        console.log("blob content type:", contentType);

        let blob = new Blob(byteArrays, {type: contentType});

        // alternative way WITHOUT chunking the base64 data
        // let blob = new Blob([atob(b64Data)],  {type: contentType});

        return blob;
  }

save the image with File.writeFile()

// save the image with File.writeFile()
savebase64AsImageFile(folderpath,filename,content,contentType){

      // Convert the base64 string in a Blob
      let data:Blob = this.b64toBlob(content,contentType,512);
      
      console.log("file location attempt is:",folderpath + filename);

      File.writeFile(
        folderpath,
        filename,
        data,
        {replace: true}
      ).then(
        _ => {console.log("write complete:")}
      ).catch(
        err => {
          console.log("file create failed:",err);
        }
      );    
  }

I have tried dozens of different decoding techniques, but the effect is the same. However, if i hard code simple text data into the writeFile() section, like so:

File.writeFile(
        folderpath,
        "test.txt",
        "the quick brown fox jumps over the lazy dog",
        {replace: true}
      )

A text file IS created correctly in the expected application location with the text string above in it. However, I’ve also noticed that whether the file is the 0 bytes PNG, or the working text file above, in both cases the “.then()” consequence clause of the File Promise never fires.

Additionally, I swapped the above method and used the Ionic 2 native Base64-To-Gallery library to create the images, which worked without a problem. However, having the images in the user’s picture gallery or camera roll is not an option for me as I do not wish to risk a user’s own pictures while marshalling / packing / transmitting / deleting the data-rendered images. The images should be created and managed as part of the app.

User marcus-robinson seems to have experienced a similar issue outlined here, but it was across all file types, and not just binary types as seems to be the case here. Also, the issue seems to have been closed:

https://github.com/driftyco/ionic/issues/5638

Anybody experiencing something similar, or possibly spot some bloody stupid error I might have caused? I’ve tried dozens and dozens of alternatives but none seem to work! This is a real showstopper for me and i’m stalled till i get some light shed on the matter!

Thanks in advance,

Shrike71