Titanium Mobile: Build an Image Uploader

Titanium Mobile: Build an Image Uploader

Tutorial Details
  • Technology: Titanium Mobile
  • Difficulty: Intermediate
  • Completion Time: 30 - 60 Minutes

This tutorial will demonstrate how to build a custom progress bar by creating an image uploader with Titanium Mobile. Specifically, the demo project will allow you to select an image from the device photo gallery and upload it to a remote server for storage. Both the Titanium Mobile code and the server-side code will be explained. Now, let’s get started!


Project Preview

The following is a preview of the cross-platform application that we will be building in this series:

Android SDK Progress Bar Article
Android SDK Progress Bar Article

Prerequisites…

This tutorial assumes that you are running Titanium Studio and have the latest SDK updates installed, which was 1.7.5 at the time of this writing. As this is tutorial is focused on the iOS platform, the reader would also benefit from having all the requirements for development on the iOS platform.


Step 1: Creating a New Project

Open Titanium Studio and create a new project, selecting the Android, iPhone, and iPad device targets only. You can name your new project whatever you like, however, we’re going to call ours “ProgressBar”. Give your new project an App ID and URL, then select the latest Titanium SDK Version. When you are done, click “Next”, and then select the Single Window Application template before clicking “Finish”.

Android SDK Progress Bar Article
Android SDK Progress Bar Article

If the “Single Window Application” template option is not available in your version of Titanium, you can import the project source code attached to this tutorial instead. Download the project files, and unzip them to the directory that you wish to work from. Then follow these steps:

  1. From the File menu, select “Import” to open the Import Project pop-up window.
  2. From the import source list, select “Titanium” and then “Import Existing Titanium Project”.
  3. In the Directory text field, browse to the folder that contains the project files you downloaded and unzipped.
  4. Click the Finish button, and a new project called “ProgressBar” will be created and made available in your Project Explorer pane.

If you haven’t already done so, now is a good time to download the project files and unzip all the images in the “Resources/assets/images” directory to your own “Resources/assets/images” directory under the new project you just created. By default, Titanium also includes two icon files in the root of your “Resources” directory called KS_nav_ui.png and KS_nav_views.png – we don’t need either, so go ahead and move both of these to the trash. You can skip this step if you have already imported the entire project using the “Import” feature.


Step 2: Creating the Interface

As the Single Window Application template follows the CommonJS structure, it may be a bit different to what you’re used to. Here, the app.js file pulls in the ApplicationWindow.js file using ‘require’, which in turn instantiates and returns our default Window object for the application. The ApplicationWindow file instantiates a new view called FirstView, which is coded within the FirstView.js file under the ‘Resources/ui’ folder. This FirstView view object is where we will be coding almost all of this tutorial. Delete any pre-generated code within the exports.FirstView = function() constructor and replace it with the following:

//FirstView Component Constructor
exports.FirstView = function() {
	// Let's hide the status bar on the iphone/ipad for neatness
	if(Ti.Platform.osname == 'iphone' || Ti.Platform.osname == 'ipad'){
		Titanium.UI.iPhone.statusBarHidden = true;
	}
	// Create object instance, a parasitic subclass of Observable
	var self = Ti.UI.createView({
		backgroundColor: '#232323'
	});
	// The view below is the background of the slider
	var progressBackgroundView = Ti.UI.createView({
		width: 300,
		height: 27,
		left: ((Ti.Platform.displayCaps.platformWidth - 300) / 2),
		top: (Ti.Platform.displayCaps.platformHeight / 2),
		visible: false,
		backgroundImage: 'assets/images/track-complete.png'
	});
	self.add(progressBackgroundView);
	//the slider will show a graphical representation of the upload progress
	//backgroundImage will reduce flicker as it doesn't redraw every width change like 'image' will
	var progressView = Ti.UI.createImageView({
		width: 0,
		height: 25,
		left: 1,
		top: 1,
		backgroundImage: 'assets/images/bar.jpg',
		borderRadius: 3
	});
	progressBackgroundView.add(progressView);
	//this label will show the upload progress as a percentage (i.e. 25%)
	var lblSending = Ti.UI.createLabel({
		width: 'auto',
		right: ((Ti.Platform.displayCaps.platformWidth - 300) / 2),
		top: ((Ti.Platform.displayCaps.platformHeight / 2) + 30),
		text: '',
		height: 20,
		font: {fontFamily: 'Arial', fontSize: 14, fontWeight: 'bold'},
		color: '#fff',
		textAlign: 'right',
		visible: false
	});
	self.add(lblSending);
	//this button will appear initially and allow the
        //user to choose a photo from their gallery
	var btnChoosePhoto = Ti.UI.createButton({
		width: 220,
		height: 35,
		title: 'Select photo for upload.',
		font: {fontFamily: 'Arial'},
		color: '#000000',
		top: (Ti.Platform.displayCaps.platformHeight / 2),
		visible: true
	});
	self.add(btnChoosePhoto);
	return self;
};

Above we have created the basic structure of our progress bar, and a button that will allow the user to open the Photo Gallery on their device and select an image that they wish to upload. Run the application in the simulator now, and you should see a single button in the middle of the screen that looks something like this:

Android SDK Progress Bar Article

Step 3: Choosing an Image From the Gallery

Now that we have our basic interface setup, let’s add an event listener and handler to btnChoosePhoto that will allow our user to choose a photo from their gallery. Type in the following code directly before the line ‘self.add(btnChoosePhoto);‘.

 btnChoosePhoto.addEventListener('click', function(e){
		Titanium.Media.openPhotoGallery({
	        success:function(event)
	        {
	            Ti.API.debug('Our type was: '+event.mediaType);
	            if(event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO)
	            {
	            	UploadPhotoToServer(event.media);
	            }
	        },
	        cancel:function()
	        {
	        },
	        error:function(err)
	        {
	        	Ti.API.error(err);
	        },
	        mediaTypes:[Ti.Media.MEDIA_TYPE_PHOTO]
	    });
	});

In this “click” event handler, we are opening the Gallery and checking within the success section of the code to see whether the media type chosen chosen was a photo by comparing the object to the constant value Ti.Media.MEDIA_TYPE_PHOTO. If this check passes, we next pass our event.media object into a new function called UploadPhotoToServer. It is here that we will perform both the file upload and show the user our progress bar as that upload occurs.


Step 4: Creating the PHP Server Code to Accept and Save our Uploaded Photo

We’re going to create our server code to remotely save our photo and return its URL using PHP. PHP is a common, well known web language supported by many common web hosting services. Create a new file called upload.php and type into that file the following code:

<?php
    //this function returns a random 5-char filename with the jpg extension
    function randomFileName()
    {
       $length = 5;
       $characters = 'abcdefghijklmnopqrstuvwxyz';
       $string = '';
       for ($p = 0; $p < $length; $p++) {
          $string .= $characters[mt_rand(0, strlen($characters))];
       }
       return $string . '.jpg';
    }
    //create the random filename string and uploads target variables
    $randomString = randomFileName();
    $target = 'uploads/';
    $target = $target . $randomString;
    if(move_uploaded_file($_FILES['media']['tmp_name'], $target))
    {
        //output the location to our image
        echo 'http://mobiletuts.example.com/progressbar/uploads/' . $randomString;
    }
    else
    {
        echo "false";
    }
?>

This basic PHP script simply takes in a POSTED file called ‘media’ and then saves it with a random, five character filename of the JPG extension, which is generated by the randomFileName() function. If we manage to save the photo successfully, the script will then echo out the remote URL location of our new photo, otherwise it will echo out ‘false’ if it fails.

Now you must save and transfer the upload.php file to your server. Additionally, you will also need to create a new folder in the same directory as your upload.php file called ‘uploads’. This is the folder where our images will be saved. Make sure the image folder has the proper write permissions (usually CHMOD 770) or our PHP script will not work.

Note that if you don’t have access to a PHP server you can always write a script in the language of your choice (.NET, Ruby, etc) that performs the same task.


Step 5: Uploading a Photo and Showing the Progress via our Custom ProgressBar

Before we create our function, there’s a couple of small things we need to do. The first is to create a variable called androidUploadProgress at the top of our exports.FirstView declaration. This will track the approximate upload progress of our file upload on Android devices. Unfortunately, at the time of writing Titanium Mobile for Android doesn’t support the progress variable during the onsendstream event, meaning we can’t calculate exactly how far through the upload we are on Android devices and must therefore use a best guess approach.

//this variable is for android to calculate the approx upload progress
var androidUploadProgress = 0;

Additionally, we need to create a new string variable in the “i18n/en/strings.xml” file which is going to hold the location of our server upload script. Add in the following string element after the existing ‘welcome’ one, replacing the URL with the location of your own PHP script.

<string name="server">http://mobiletuts.example.com/progressbar/upload.php</string>

Let’s create our UploadPhotoToServer function now, by typing in the following code in your FirstView.js file. The best place for this function is underneath the section near the top that hides the statusBar on iOS devices. This starts with if(Ti.Platform.osname == ‘iphone’. This function is going to check whether our user is online, and, if so, create a new HTTPClient that will send the media data to our PHP server where it will be saved and the full URL of the uploaded image returned.

This is a lengthy function and forms the “meat” of our Titanium application, so let’s step through it piece by piece. First of all, let’s create the function itself, along with the initial code which is going to hide the “Choose Photo” button and instead show the user our progress bar:

        //this function will take in a 'media' object (a photo from the gallery in this case)
	//and will upload it to our server via the PHP script. On a successful upload, our
	//server will return the new HTTP path of the image we uploaded, which we can then load
	//in the Safari/web browser so the user can view it.
	function UploadPhotoToServer(media){
		 if (Titanium.Network.online == true)
                 {
	           self.children[0].show(); //show the uploading slider progress bar
	           self.children[0].children[0].width = 0; //make sure the default value is zero
	           self.children[1].show(); //show the uploading label
	           self.children[1].text = 'Uploading photo, please wait...'; //set the label to default value
	           self.children[2].hide(); //hide the select photo button
		}
		else
		{
			alert('You must have a valid Internet connection in order to upload this photo.');
		}
        }

You will notice that we are accessing our display components by referring to their index within the FirstView’s children object. We could have also simply declared our display components at the top of our FirstView.js file and then instantiated them, however, we’ve chosen to do things this way so that you understand how to access the child of a parent view. Next, we need to instantiate our XHR HttpClient object and create all of the event handlers necessary in order to track when our HttpClient upload is running and if it fails or has succeeded. Add the following code below what you previously typed. Note how we are accessing the “server” string property set earlier in the tutorial from our strings.xml file using L(“server”), and how the parameter object our HttpClient post is sending is called “media“, which exactly matches the “media” FILE object expected by our PHP script.

var xhr = Titanium.Network.createHTTPClient();
xhr.onerror = function(e){
	Ti.API.info('IN ERROR ' + e.error);
	alert('Sorry, we could not upload your photo! Please try again.');
  };
 xhr.onload = function(){
	Ti.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState);
 };
xhr.onsendstream = function(e){
	Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress);
};
// open the client
xhr.open('POST', L('server')); //the server location comes from the 'strings.xml' file
// send the data
xhr.send({
	media: media,
});

Now let’s edit the onsendstream event handler function. In this function we are going to calculate the current upload progress of our file, and extend the width of the progress bar accordingly. Note that we do this slightly different for Android than we do for iOS, as the onsendstream property “progress” is currently not implemented for Android.

xhr.onsendstream = function(e){
	Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress);
	if(Ti.Platform.osname == 'android')
	{
	     //android doesn't support the "progress" variable during onsendstream yet :(
	     //we're going to dummy up a progress value for this based on each packet being about 2.5% of the total upload progress
	     //it won't be totally accurate, but it will give the user a good indicator that the upload is working
	    if(androidUploadProgress < 1) {
	         androidUploadProgress += 0.025;
		 self.children[1].text = 'Uploading photo, please wait... ' + (Math.round(androidUploadProgress * 100)).toString().replace(".","") + '%';
	         self.children[0].children[0].width = Math.round(298 * androidUploadProgress);
	   }
	 }
	 else
	  {
	      //else on ios devices, calculate the progress of the upload using e.progress
	      if(Math.round(e.progress * 100) <= 100) {
		     self.children[1].text = 'Uploading photo, please wait... ' + (Math.round(e.progress * 100)).toString().replace(".","") + '%';
		     self.children[0].children[0].width = Math.round(298 * e.progress); //set the slider value to the nearest whole integer (ie 25%, not 24.95%)
	      }
	   }
};

Try running your application in the simulator now, and after choosing a Gallery photo your progress bar should appear and should increase in size as the file uploads. It should look just like the one below:

Android SDK Progress Bar Article

Step 6: Finishing Up

Finally, we need to extend the unload event handler function. In it, we need to ensure that the progress bar shows 100% (especially for Android), and we need to determine whether the file successfully uploaded to the server or not by checking whether the responseText property is set to “false”. If it’s not, we can then assume that everything worked A-OK and present our users with a confirmation alert dialog where they can choose to open their newly uploaded image in the web browser. We’ll then reset our layout and all our object properties when ready for the next photo upload.

 xhr.onload = function(){
	Ti.API.info('IN ONLOAD ' + this.status + ' readyState ' + this.readyState);
	if(this.responseText != 'false')
  {
	  var url = this.responseText; //set our url variable to the response
	  self.children[0].children[0].width = 298;  //set the progress to 100% (298px based on our design)
	  //if we successfully uploaded, then ask the user if they want to view the photo
	   var confirm = Titanium.UI.createAlertDialog({
	        title: 'Upload complete!',
	        message: 'Open your image in the browser?',
	         buttonNames: ['Yes', 'No']
	  });
         confirm.addEventListener('click', function(conEvt) {
		//if the index selected was 0 (yes) then open in safari
		 Ti.API.info(conEvt.index);
		 if(conEvt.index === 0){
		    //open our uploaded image in safari
		     Ti.Platform.openURL(url);
	      }
		//reset the upload button
		 self.children[0].hide(); //hide the status bar
	         self.children[1].hide(); //hide the status label
	         self.children[2].show(); //show the upload button again
	         androidUploadProgress = 0; //reset the android progress value
	   });
	   confirm.show();
	}
	else {
		alert('Whoops, something failed in your upload script.');
	        self.children[0].hide(); //hide the status bar
	        self.children[1].hide(); //hide the status label
	        self.children[2].show(); //show the upload button again
	        androidUploadProgress = 0; //reset the android progress value
	}
};

That’s it! Run the application now either in the simulator (Android or iPhone) or on your device. You should still see the file upload progress meter animate from 0-100%, and when completed an alert dialog should appear like the one below, giving you the option to view the newly uploaded photo in the browser.

Android SDK Progress Bar Article
Android SDK Progress Bar Article

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.demogar.com demogar

    Great and simple tutorial. I hope you publish more tutorials about Titanium Mobile.

    • http://boydlee.com Boydlee
      Author

      I am hoping to start publishing some for MobileTuts on a regular basis. Is there any Titanium tutorials you’d like to see in particular?

      • http://www.swf.ch André

        I’d like to see a tutorial on how to update a local database with data from a webserver. (replace existing with an updated one)

  • Ayo

    Nice tutorial. Where and how did you install the project templates in Titanium Studio? The only one I have is Geolocation mobile template.

    Thanks…

    • http://boydlee.com Boydlee
      Author

      Hi Ayo,

      I had thought it was simply part of the new Titanium Studio update, however perhaps it is part of the Beta software updates. Go into Titanium Studio, and in the main menu click “Titanium Studio” -> “Preferences” and then “Software Updates” and tick the Beta updates option.

      http://i.imgur.com/EXXv9.png

      Otherwise you can always import the project files and use those.

      • Ayo

        Thanks. I figured it out. It was part of the Appcelerator github: titanium_mobile.ruble.

        https://github.com/appcelerator/titanium_mobile.ruble

        How to install

        Install Studio
        Download titanium_mobile.ruble.zip
        Unzip to ~/Documents/Aptana Rubles
        Rename the folder to titanium_mobile.ruble
        The name of the folder doesn’t really matter, just so long as it has the ruble extension
        Enjoy!

        Looking forward to more tutorials and your cookbook…

    • http://boydlee.com Boydlee
      Author

      Hi Ayo,

      My cookbook has just been released if you’re interested, you can grab it from http://ow.ly/82Brx – it’ll also be on Amazon.com and most other major retailers websites by the end of December.

      Cheers,
      Boydlee

  • http://makeappseasy.com alfonso

    yea please more tuts for appcelerator

  • macCesar

    Finally. A titanium tutorial. Please, more tuts like this

  • http://twitter.com/#!/Mahmkamal Mahmoud Kamal

    Good Job ;)

  • Amar

    Hi ,

    iam a newbee to Ti world
    can u give me an example regarding how to open a titanium mobile app from the browser url.

    i need to open the app from an sms with link.

    help on custom url schema is very much appreciated.

    thanks in advance..

  • Dika

    why I can not upload in my own localhost ? I have used your php code. and I have changed the url to be: “localhost/test/upload.php”
    I hope u can help me.

    • http://www.tipsyandtumbler.co.uk Boydlee
      Author

      Hi Dika,

      Your mobile phone has no concept of “localhost” and neither does the iPhone simulator. Localhost is the default name given to your computers “self” IP address, but no such host exists on mobile. If you want to test the code via your localhost PHP installation, then you will need to refer to it by your computers IP address (e.g. 192.168.0.5).

      On Windows, you can get this value by going to the command prompt and typing “ipconfig”.

      On Mac, you can get the IP address by going to Applications -> Utilities -> Network Utility. The IP will be on the first tab (you might have to change the drop down list to whichever network device you’re currently using).

      Thanks,
      Boydlee

      • Dika

        Thank you very much for your help. I really appreciate it.

  • Eric

    Is there a better way to code than using the children[0], related?

    e.g. self.children[0].hide(); //hide the status bar
    self.children[1].hide(); //hide the status label
    self.children[2].show();

    very hard to understand and not descriptive, index may changed too. Any tips? Thanks.

    • Wally

      I’d like to know the same. It seems there would be a more convenient and predictable way to do this. Kind of hard to follow if I changed the order of any of the things I added.

  • Dennis

    I did buy your cookbook, though quite good it lacks a lot of the things which make a mobile app.. Not a whole lot in there about images.. If you’re creating tutorials, might I suggest one for images, selecting, camera, filters, image scrollers, swipe, etc..?

  • http://erickar.be Erick

    Great tutorial! I tested out this app on my phone (built it, compiled it in Titanium studio) however in the console I am getting an error:

    IN ERROR Error Domain=ASIHTTPRequestErrorDomain Code=1 “A connection failure occurred” UserInfo=0x3f0e10 {NSUnderlyingError=0x6bf4b50 “The operation couldn’t be completed. Cannot allocate memory”, NSLocalizedDescription=A connection failure occurred}

    In my simulator, I was able to upload a photo, however on the phone I got this error. I am running an iPhone 4, so the images are pretty large. Could this be the case? If so, how would I fix it? The loader gets to about 20% everytime before it times out.

    Thanks!

  • José Luís

    Thumbs up for this article!

    Please add more tutorials!

  • Abe

    What if we need to send the img encoded?