Upload Photos to Your Web Site With
Your SmartPhone and the Compact Framework

by Peter A. Bromberg, Ph.D.

Peter Bromberg

" I claim that in losing the spinning wheel we lost our left lung. We are, therefore, suffering from galloping consumption. The restoration of the wheel arrests the progress of the fell disease. " -- Gandhi

Recently my wireless provider, Cingular, figured out that ATT Wireless (which they had acquired) had a good deal going with the AudioVox SMT5600 and finally got around to offering the same $199 (with mail-in $50 rebate) deal to existing and new Cingular customers. Being eminently familiar with this incredible phone, I jumped on board, and added a 256MB miniSD storage card for another $45 from Best Buy.



Of course the first thing you do is set up ActiveSync ( version 4.0 now works with x64 platforms and supports Windows Media and a whole bunch of other cool stuff), you put some music on there, you play around with the camera, videocam, voice recorder, make some nice voice tag commands to your favorite programs and what not. Windows Media Player 10 is cool. It even plays video off the web.

The next thing you think about is the fact the this thing comes preloaded with the Compact Framework SP1 in ROM. That's nice! The next thing I thought about was "What can I do that's cool with Visual Studio.NET 2005"? The result of my first "experiment" is what you see here. I figured that with a camera, probably the first thing I'd want to do is be able to take photos and upload them to my website and be able to view them or share them on the web.

Fortunately, there's enough built-in functionality with HttpWebRequest to handle all this, although it can be a bit clunky at times. Unfortunately, the SmartPhone form factor eliminates so many familiar controls such as OpenFileDialog that you have to resort to workarounds. One good place to look so as not to have to "reinvent the wheel" is Neil Cowburn's (MVP) site , opennetcf.org with their SmartDevice Framework libraries. They have a "Dialogs" solution which includes FileListDialog, FolderBrowser, and OpenFileDialog classes that pretty much work "out of the box". Kudos to Neil and his buddies for the great job they've done to support the Compact Framework!

So essentially what we need to do is the following:

1) Build some sort of config.xml arrangement and the ability to edit it so people can put in a custom Url to the aspx page that receives and display photos.

2) Build a page that can handle both uploads and viewing the photos on the SmartPhone in Internet Explorer.

3) Build a Folder Browser/ OpenfileDialog arrangement so you can browse your device for a photo to upload.

4) Allow the user to send (via HTTP POST) the selected file as a byte stream.

I won't bore you with the OpenNetCF dialog classes as they are freely downloadable and our MVP friends there have created excellent documentation. I will share my two main forms and their code: my Upload form, and my ConfigForm (for working with the target URL from the config.xml file). Finally, at the bottom, you'll be able to download the entire Visual Studio.NET 2005 Beta 2 solution including a CAB project, or you can even download the CAB installer directly to your SmartPhone and install it with the provided link, if you would rather just "try it out" first. Be aware that if you do, the pre-configured upload folder is on my ASP.NET 2.0 "Playground" site. If too many photos get uploaded there, I will have to disable it. One or two, just for a test, no problem.

First, let's take a look at how to put together an ASP.NET page that can handle BOTH uploading and display of photos:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        byte[] theData = null;
        if (Request.ServerVariables["REQUEST_METHOD"].ToString().ToUpper() == "POST")
        {
            theData = Request.BinaryRead(Request.ContentLength);          
            string picName = DateTime.Now.Ticks.ToString() + ".jpg";          
            FileStream stm = new FileStream(Server.MapPath(picName), 
System.IO.FileMode.CreateNew); stm.Write(theData, 0, (int)theData.Length); stm.Close(); } else { DirectoryInfo dir = new DirectoryInfo(Server.MapPath(".")); getDirsFiles(dir); } } public void getDirsFiles(DirectoryInfo d) { //create an array of files using FileInfo object FileInfo [] files; //get all jpeg files for the current directory files = d.GetFiles("*.jpg"); //iterate through the directory and print the files Response.Write("<ul>"); foreach (FileInfo file in files) { //get details of each file using file object String fileName = file.Name; String fileSize = file.Length.ToString(); String fileExtension =file.Extension; String fileCreated = file.LastWriteTime.ToString(); Response.Write("<li><a Href="+fileName +"><img src="+fileName+" border="0"></a></li>"); } Response.Write("</ul>"); } }

You can see above that it is easy to turn this into a dual-purpose page; if the Request method is POST, we are getting an upload. If it's a GET, we're being asked to display the jpegs. Your account that this runs under needs write permissions, or you won't get very far.

Now let's look at the Config Form. It simply loads the config.xml file and uses a "poor man's" Configuration method to get the sole entry. I have, however, kept the schema consistent with the standardized Configuration schema for .NET:

private void ConfigForm_Load(object sender, EventArgs e)
  {
  string path = configPath+ @"\Config.xml";
  configDoc.Load(path);
  XmlNode nod = configDoc.GetElementsByTagName("add")[0];
  this.textBox1.Text = nod.Attributes["value"].Value;   
  }

  private void menuItem1_Click(object sender, EventArgs e)
  {
   this.Hide();
   this.Close();
   Form1 frm = new Form1();
   frm.Show();   
  }

  private void menuItem2_Click(object sender, EventArgs e)
  {
   
  }

  private void menuItem3_Click(object sender, EventArgs e)
  {
   XmlNode nod = configDoc.GetElementsByTagName("add")[0];
   nod.Attributes["value"].Value = this.textBox1.Text;
   string path = configPath + @"\Config.xml";
   configDoc.Save(path);
  }

  private void menuItem4_Click(object sender, EventArgs e)
  {
   this.Hide();
  }

All I do here is load the Xml file get the single element by Tag name, and show the "value" attribute in the textbox. After you edit it, it just saves the xml file. Easy!

Now the main Form:

public void UploadFileBinary(string localFile, string uploadUrl)
  {
            try
            {

                FileStream rdr = new FileStream(localFile, FileMode.Open);
                byte[] inData = new byte[4096];
                int totbytes = 0;
                MemoryStream postData = new MemoryStream();
                int bytesRead = rdr.Read(inData, 0, inData.Length);
                while (bytesRead > 0)
                {
                    postData.Write(inData, 0, bytesRead);
                    bytesRead = rdr.Read(inData, 0, inData.Length);
                    totbytes += bytesRead;
                }
                rdr.Close();
                postData.Position = 0;
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uploadUrl);
                req.Method = "POST";
                req.ContentLength = (long)postData.Length;
                using (Stream s = req.GetRequestStream())
                {
                    s.Write(postData.ToArray(), 0, (int)postData.Length);
                    postData.Close();
                }               
                WebResponse resp = req.GetResponse();
                resp.Close();
            }
            catch (Exception ex)
            {                
            }
            finally
            {
            }

  }  
        
        private void menuItem1_Click(object sender, EventArgs e)
        {
            
            OpenNETCF.Windows.Forms.OpenFileDialog dlg = 
new
OpenNETCF.Windows.Forms.OpenFileDialog(); DialogResult res = dlg.ShowDialog(); if (res == DialogResult.OK) this.localFileTextBox.Text = dlg.FileName; } private void menuItem2_Click(object sender, EventArgs e) { } private void menuItem3_Click(object sender, EventArgs e) { // send file UploadFileBinary(this.localFileTextBox.Text, this.urlTextBox.Text); Cursor.Current = Cursors.Default; MessageBox.Show("Upload Successful"); } private void menuItem5_Click(object sender, EventArgs e) { ConfigForm frm = new ConfigForm(); frm.Show(); } private void Form1_Load(object sender, EventArgs e) { configPath = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly(). [break]
GetModules()[0].FullyQualifiedName); string path = configPath + @"\Config.xml"; configDoc.Load(path); XmlNode nod = configDoc.GetElementsByTagName("add")[0]; this.urlTextBox.Text = nod.Attributes["value"].Value; } private void menuItem6_Click(object sender, EventArgs e) { Application.Exit(); } }

Here are a couple of screen captures that illustrate the UI that goes with the above code:

UI of the Folder / File Browser with SoftkeyContext menu
UI of the main form with Context Menu

You can look at any photos that have been uploaded to my playground site here.

You can download the complete Visual Studio 2005 Solution, including the CAB Installer project, here.

You can download the CAB file directly to your device and install it here.

Conclusion: The Microsoft SmartPhone form factor and features are hot, and they will be getting hotter for quite some time to come. Developers interested in targeting this market would be well-advised to start studying, because there is a good market for compatible software of all types. Java game developers should take note as the Audiovox SMT5600 also supports midlets! There is also GAPI, a pretty sophisticated Graphics API available, and there are .NET wrappers for same.

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: