Windows Phone select and upload image to a website over HTTP POST
June 4, 2013 Leave a comment
You have often seen photo sharing websites where you can attach images and after clicking the submit button the local images will be uploaded to the server. It is implemented by using HTML forms which send HTTP requests automatically. But if your application is not a web application written in HTML, you should write HTTP requests by yourself. You should do that in Windows Phone too.
So the full sequence consists of 2 actions: 1 – select an image, 2 – upload it to the server.
At first, you should select an image from the gallery.

The code is simple, just use the built-in PhotoChooserTask class:
public void AttachFile()
{
var chooseTask = new PhotoChooserTask();
chooseTask.ShowCamera = true;
chooseTask.Completed += (s, e) => this.OnChoosePhotoCompleted(e);
chooseTask.Show();
}
private void OnChoosePhotoCompleted(PhotoResult result)
{
if (result.TaskResult == TaskResult.OK)
{
this.HasPhoto = true;
this.PhotoPath = new Uri(result.OriginalFileName);
this.PhotoName = Path.GetFileName(result.OriginalFileName);
using (var fileBytes = new MemoryStream())
{
result.ChosenPhoto.CopyTo(fileBytes);
this._photoBytes = fileBytes.GetBuffer();
}
}
}
Then you should use the HttpWebRequest class to post the selected image to the server. This class alone is not enough, so you should write lots of extra code to make it work. I have implemented a special class for this purpose, the code is based on this answer on stackoverflow.com, you can just copy it to your project.
public class HttpPostTask
{
private readonly string _boundary = "----------" + DateTime.Now.Ticks.ToString();
private bool _isCancelled;
private bool _isExecuted;
public HttpPostTask(string url, Dictionary<string, object> parameters, Action<string> onCompleted)
{
this.Url = url;
this.Parameters = parameters;
this.OnCompleted = onCompleted;
}
public string Url { get; set; }
public Dictionary<string, object> Parameters { get; set; }
public Action<string> OnCompleted { get; private set; }
public Action<string> OnError { get; set; }
public void Execute()
{
if (this._isExecuted)
{
throw new NotSupportedException();
}
this._isExecuted = true;
HttpWebRequest httpWebRequest = WebRequest.CreateHttp(this.Url);
httpWebRequest.Method = "POST";
httpWebRequest.ContentType = string.Format("multipart/form-data; boundary={0}", this._boundary);
httpWebRequest.BeginGetRequestStream(new AsyncCallback(this.GetRequestStreamCallback), httpWebRequest);
}
public void Cancel()
{
this._isCancelled = true;
}
protected void InvokeOnErrorHandler(string message)
{
if (this.OnError != null)
{
this.InvokeInUiThread(() => this.OnError(message));
}
}
protected void InvokeInUiThread(Action action)
{
if (!this._isCancelled)
{
Deployment.Current.Dispatcher.BeginInvoke(action);
}
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
Stream postStream = request.EndGetRequestStream(asynchronousResult);
this.WriteMultipartObject(postStream, this.Parameters);
postStream.Close();
request.BeginGetResponse(new AsyncCallback(this.GetResponseCallback), request);
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// get the response
HttpWebResponse response;
try
{
response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
}
catch (Exception e)
{
if (e.InnerException != null && e.InnerException.Message.StartsWith("[net_WebHeaderInvalidControlChars]"))
{
// not an exception, everything is ok
this.InvokeInUiThread(() => this.OnCompleted(null));
}
else
{
this.InvokeOnErrorHandler("Unable to post data.");
}
return;
}
if (response.StatusCode != HttpStatusCode.OK)
{
this.InvokeOnErrorHandler((int)response.StatusCode + " " + response.StatusDescription);
return;
}
// response stream
using (Stream stream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(stream))
{
string str = reader.ReadToEnd();
this.InvokeInUiThread(() => this.OnCompleted(str));
}
}
}
private void WriteMultipartObject(Stream stream, Dictionary<string, object> data)
{
StreamWriter writer = new StreamWriter(stream);
if (data != null)
{
foreach (var entry in data)
{
this.WriteEntry(writer, entry.Key, entry.Value);
}
}
writer.Write("--");
writer.Write(this._boundary);
writer.WriteLine("--");
writer.Flush();
}
private void WriteEntry(StreamWriter writer, string key, object value)
{
if (value != null)
{
writer.Write("--");
writer.WriteLine(this._boundary);
if (value is byte[])
{
byte[] ba = value as byte[];
writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""; filename=""{1}""", key, "image.png");
writer.WriteLine(@"Content-Type: application/octet-stream");
writer.WriteLine(@"Content-Length: " + ba.Length);
writer.WriteLine();
writer.Flush();
Stream output = writer.BaseStream;
output.Write(ba, 0, ba.Length);
output.Flush();
writer.WriteLine();
}
else
{
writer.WriteLine(@"Content-Disposition: form-data; name=""{0}""", key);
writer.WriteLine();
writer.WriteLine(value.ToString());
}
}
}
}
The usage of this class is simple. I use the http://posttestserver.com website to which I post images, but you can use a more appropriate site like Google Picasa or Imgur.
string url = "http://posttestserver.com/post.php?dir=wp7posttest";
var parameters = new Dictionary<string, object>();
parameters.Add("name", this.PhotoName);
parameters.Add("photo", this._photoBytes);
this._currentPostTask = new HttpPostTask(url, parameters, this.OnPostCompleted);
this._currentPostTask.OnError = this.OnPostError;
this._currentPostTask.Execute();
The website http://posttestserver.com returns a string with a link to the posted data, other website will return different responses.
The working sample application which I used in this post you can download here: HttpPostSample.zip


















