Posting A Photo From Windows Phone 7 To A Web Service
Recently I needed a way to upload a photo taken from a Windows Phone 7 to a public web service. The service then takes the photo’s bits and stores it. So I thought I would share the basics to this process today.
Download The Code
I am not going to get into the details of how to take a photo from your Windows Phone 7 application, Nancy Strickland has done a really nice video tutorial about working with the WP7 camera. I am going to assume you understand the basics of using the phone’s camera, which means you will have an event handler for the CameraCaptureTask.Completed event.
Inside the Completed event handler you need to decode the PhotoResult.ChosenPhoto to a JPG. The ChosenPhoto is just a Stream object representing the bytes that compose your photo. Of course for the emulator this is just going to be a simple white background with a small black block located somewhere on the edge of the image.
One thing I did learn when working through this solution is the bytes returned in the ChosenPhoto property are not a viable photo on their own. I am not sure exactly why, but I realized without going through the decode process the bytes are simply empty when they are written to disk.
Use the PictureDecoder.DecodeJpeg method to decode the Stream to a WriteableBitmap, which in this case is a JPEG. A WriteableBitmap is a special Bitmap that can be written to and updated during its lifetime. In my sample I set the source of an Image control to the WriteableBitmap and pass the bitmap to another method called PostImageToWebService. The image control just gives you visible feedback and is not actually used to upload the photo.
void camTask_Completed(object sender, PhotoResult e) {
var picture = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
image1.Source = picture;
PostImageToWebService(picture);
}
The PostImageToWebService method takes the WriteableBitmap and saves a copy of it to isolated storage. I chose to do this because I needed a way to get the images bytes to pass the web service and I did not see a good way to do this without creating a real file. I am sure there is a slightly better methodology, maybe a MemoryStream, but this worked for me.
The other side of this sample is a simple web service end point called SaveImage that accepts a fileName and byte array. The SaveImage endpoint just passing the variables to the writeByteArrayToFile method.
public void SaveImage(string fileName, byte[] photo) {
writeByteArrayToFile(
Path.Combine(@"c:\TempImg", fileName), photo);
}
The writeByteArrayToFile method simply writes the bytes to a new file. For simplicity sake I just have it write to a folder off the root called TempImg.
public void writeByteArrayToFile(string fileName, byte[] buffer) {
using (FileStream fs =
new FileStream(fileName,
FileMode.Create, FileAccess.ReadWrite)) {
fs.Write(buffer, 0, (int)buffer.Length);
}
}
Back to the phone application. Because this is a Silverlight application all calls to a service endpoint must be done asynchronously. So you need to have an event handler defined for the SaveImageCompleted event. Then you can call SaveImageAsync, not SaveImage. The fileName is a class level variable but the value is set in the PostImageToWebService method. To keep things random I chose to create a new Guid and use this value in the file name.
private void PostImageToWebService(WriteableBitmap picture) {
fileName = "tempPhoto-" + Guid.NewGuid().ToString() + ".jpg";
SavePictureToIsolatedStorage(picture, fileName);
var client = new ServiceReference1.Service1Client();
client.SaveImageCompleted +=
new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>
(client_SaveImageCompleted);
client.SaveImageAsync(fileName, GetPhotoBytes(fileName));
}
Before the call to the web service is made, a call to the SavePictureToIsolatedStorage. This method saves the photo to the application’s Isolated Storage area. The only think I will point your attention to in this method is the use of the SaveJpeg method, which of course saves the image to the file system. This is actually an extension method included in the .NET framework.
public void SavePictureToIsolatedStorage(
WriteableBitmap chosenPhoto, string fileName) {
if (chosenPhoto != null) {
using (var appStorage =
IsolatedStorageFile.GetUserStoreForApplication()) {
if (!appStorage.DirectoryExists(tempfolder)) {
appStorage.CreateDirectory(tempfolder);
}
using (var isoStream =
appStorage.OpenFile(
String.Format(@"{0}\{1}", tempfolder, fileName),
FileMode.OpenOrCreate)) {
chosenPhoto.SaveJpeg(isoStream, chosenPhoto.PixelWidth,
chosenPhoto.PixelHeight, 0, 100);
}
}
}
}
Back in the PostImageToWebService method The photo’s bytes are retrieved using a method called GetPhotoBytes. This method actually loads the file from the file system, grabs the file’s bytes and returns them as an array.
public byte[] GetPhotoBytes(string fileName) {
using (var appStorage =
IsolatedStorageFile.GetUserStoreForApplication()) {
IsolatedStorageFileStream isoStream =
appStorage.OpenFile(
String.Format(@"{0}\{1}", tempfolder, fileName),
System.IO.FileMode.Open);
byte[] buffer = new byte[isoStream.Length];
isoStream.Read(buffer, 0, (int)isoStream.Length);
isoStream.Close();
return buffer;
}
}
Ok, let’s wrap this up. Once the image has been uploaded to the service the phone application will fire the SaveImageCompleted event handler. For the sample I display a message box to indicate its done. But after that I clean up the file created in the app’s isolated storage area.
void client_SaveImageCompleted(object sender,
System.ComponentModel.AsyncCompletedEventArgs e) {
if (e.Error != null) {
MessageBox.Show(e.Error.Message,
"Error", MessageBoxButton.OK);
} else {
MessageBox.Show("Picture Uploaded");
}
using (var appStorage =
IsolatedStorageFile.GetUserStoreForApplication()) {
appStorage.DeleteFile(
String.Format(@"{0}\{1}", tempfolder, fileName));
}
}
Conclusion
Photo’s are a big part of the new mobile lifestyle. Making your mobile application leverage the phone’s camera feature to allow immediate uploads can be a big reason your application succeeds or fails. I hope this simple sample helps getting you going the right direction. Saving a temporary file to the Isolated File Storage area does not make me feel great, but I had a hard time using other ideas. So maybe you can try other methods out to see if there is a more efficient method.
Download Code