While working through one of my personal projects I’ve figured out how to load an image from an iOS device’s Library. There are two steps – first use the Camera plugin to provide a UI for the user to select a file. The next is to take the file path the Camera plugin provides and use the File plugin to load it.

Requirements

This was tested via PhoneGap Build using the following setup:

  • CLI 6.3.0
  • iOS deploy target: 9.0.0
  • Camera plugin version 2.2.0
  • File plugin version 3.0.0

If you’re using PhoneGap Build this is what should be added to your config.xml





Using the Camera Plugin to Access the Library

It might seem counter intuitive to use the Camera plugin since it seems logical to first look at the File API to look for files… unlike the File API where you would need to write your own file browser and UI, the Camera Plugin uses native functionality and so makes it trivial to pick an image from a user’s Library. The Camera plugin will present a native UI to the end-user so that they can navigate their Library’s folder structure to locate the image they want to use and in the end provide a path to that image on the device.

This code will do what is described above:

   navigator.camera.getPicture(
      function (fileURI){
         console.log(fileURI);
         /* remove the comment below to use with
          * the rest of the code on this page
          */
         //convertPath(fileURI);
      },
      function (err){
         // fail
         console.log('fail',err);
      },
      {
         allowEdit: true,
         correctOrientation: true,
         destinationType: Camera.DestinationType.FILE_URI,
         sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
         targetHeight: window.innerHeight,
         targetWidth: window.innerWidth
      }
   );

Literally copy and paste the above, here are the things to note about the configuration object:

  • allowEdit – this is a flag that tells the native Library picker UI to allow scaling/positioning of the resource that the user selects.
  • correctOrientation – as it implies, use the image in the correct orientation relevant to how the device is being held
  • destinationType – this is the part that tells the plugin to return the path to the image
  • sourceType – tells the plugin to display UI to allow the user to select the image from the library
  • targetHeight – the desired height of the image – iOS creates a temporary image and passes that path back to you based on any edits and the Height and Width settings. Here I just assume that you would want an image that is the size of the viewport.
  • targetWidth – see above

That’s it. Dead simple. Now we need to load up the file using the path that the Camera plugin returns which requires the use of the File plugin.

Using the File plugin to Load an Image

This part is trickier and the source of much frustration among developers – during my search for documentation there was no single source that explained how this should work. I was left to putting the parts together from various sources as the “official” documentation didn’t directly explain how to do it. Anyway, I’ll do the explaining here within the code comments.

In short, these are the steps that result in a Base64 serialization of the image from which you can do whatever you like:

  1. Convert the image path to a file Entry Object
  2. Pass the FileEntry Object to a function that converts it to a File Object
  3. Pass the File Object to a FileReader to read the file
  4. Handle the response containing the image data

Here is all of the code:

   /**
    * This takes a file:// URI and creates a file entry object. The operation is asynch,
    * so the resulting fileEntry object is passed to the success callback.
    * @type {Function}
    * @name convertPath
    * @param {String} fileURI - the file:// path to the resource
    * @return {} Returns nothing
    */
    function convertPath(fileURI){
        window.resolveLocalFileSystemURL(
            fileURI,
            function(fileEntry){
                getFileSuccess(fileEntry);
            }
        );
    }

   /**
    * This starts the read process via the file entry object. This is asynch, so the file is passed to the success callback
    * @type {Function}
    * @name getFileSuccess
    * @param {Object} fileEntry - the file entry object
    * @return {} Returns nothing
    */
    function getFileSuccess(fileEntry){
        fileEntry.file(
            readFile, // success
            function(err){ // failure
                console.log('Failed to get file.',err);
            }
        );
    }

   /**
    * This creates a file reader using the file object that is passed to it.
    * Note how similar this is to programatically creating an image and loading data into it.
    * @type {Function}
    * @name readFile
    * @param {Object} file - file object
    * @return {} Returns nothing
    */
    function readFile(file){
    	console.log('got file...',file);
        var reader = new window.FileReader();
        reader.oneerror = function(e){
            console.log('FileReader Error: ',e.target.result);
        };
        reader.onloadend = function(fileObject) {
            console.log('we have the file:',fileObject);
            console.log('the image data is in fileObject.target._result');
        };
        reader.readAsDataURL(file);
    }

You can use the fileObject.target._result to populate the background of a div, for example:

$('#myDiv').css('background-image','url:(' + fileObject.target._result + ')');

Or insert it into a canvas:

   var image = new Image();
   var canvas = document.getElementById('canvas');
   var context = canvas.getContext('2d'); //retrieve context
   
   image.onload = function(){
      context.drawImage(this, 0, 0,_canvas.width, _canvas.height);
   }
   image.src = fileObject.target._result; // load the image data

It’s worth noting that of course you’ll need the appropriate styling for your DIVs if using the resulting image data as a background image. Also, if loading the data into a canvas your aspect ratio may be off – you’ll need to figure out how to scale the data to fit the canvas without distortion.