How to backup your Windows Phone app data to a server such as GoDaddy
Here is a complete step-by-step guide to create a WCF backup service for your Windows Phone applications.
You can implement your own hosting service with a cheap hosting server like GoDaddy. You can find deal at $5/month. Personally, I find it more professional saving your application data to a private server or in a cloud solution instead of relaying on SkyDrive.
To accomplish this, two things are required:
A- Implementing the service on your server.
B- Implementing the client code to send your data.
Alternatively, you can download the complete solution at the end of the article.
A) Server side
1- Let’s start with the hardest part. After you find a hosting server that supports the framework .NET 4.0, you create the WCF Service Application:
2- Rename the file IService1.cs to IFileUploader.cs
3- Replace the content of the IFileUploader.cs with:
using System.Runtime.Serialization; using System.ServiceModel; namespace FileUploadService { [ServiceContract] public interface IFileUploader { // Returns null when there is no error, otherwise it is the exception message. [OperationContract] string Upload(UploadFile uploadFile); } [DataContract] public class UploadFile { // Don't forget to set the variable FileUploadDirectoryWithReadWritePermission in your Web.config and set read/write permission in your web hosting. // RelativeDirectory should be in the form as /Path1/Path2/ or null for the root directory. [DataMember] public string RelativeDirectory { get; set; } [DataMember] public string FileName { get; set; } [DataMember] public byte[] Content { get; set; } } }
This interface contains only the Upload method. The Upload needs a UploadFile which contains only 3 properties: RelativeDirectory, FileName and Content.
4- Rename the file Service1.svc to FileUploader.svc.
5- Replace the content of FileUploader.svc with:
using System; using System.Configuration; using System.IO; using System.Web.Hosting; namespace FileUploadService { public class FileUploader : IFileUploader { #region IFileUploader Members public string Upload(UploadFile uploadFile) { string message = null; try { string path = HostingEnvironment.MapPath(string.Format("~/{0}",ConfigurationManager.AppSettings["FileUploadDirectoryWithReadWritePermission"])); if (path != null) { if (uploadFile.RelativeDirectory != null && uploadFile.RelativeDirectory.StartsWith("/")) { path = string.Concat(path, uploadFile.RelativeDirectory); } if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } if (!string.IsNullOrEmpty(uploadFile.FileName)) { using (FileStream fileStream = File.Open(Path.Combine(path, uploadFile.FileName), FileMode.Create)) { using (var binaryWriter = new BinaryWriter(fileStream)) { binaryWriter.Write(uploadFile.Content); } } } } } catch (Exception exception) { message = exception.Message; } return message; } #endregion } }
6- Replace the content of the Web.config with:
<?xml version="1.0"?> <configuration> <appSettings> <add key="FileUploadDirectoryWithReadWritePermission" value="/backup/" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.0" /> <customErrors mode="Off" /> </system.web> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="FileUploadBehavior"> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="FileUploadBehavior" name="FileUploadService.FileUploader"> <endpoint address="" name="basicHttpStream" binding="basicHttpBinding" bindingConfiguration="httpLargeMessageStream" contract="FileUploadService.IFileUploader" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <bindings> <basicHttpBinding> <binding receiveTimeout="00:10:00" sendTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00" name="httpLargeMessageStream" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"> <readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" /> </binding> </basicHttpBinding> </bindings> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true" /> </system.webServer> </configuration>
This file has two important sections:
– The parameter FileUploadDirectoryWithReadWritePermission is used by the file FileUploader.svc and it will be used in your public folder later.
<appSettings> <add key="FileUploadDirectoryWithReadWritePermission" value="/backup/" /> </appSettings>
– The section about the length of the service parameters: You don’t need to understand everything here. It just works as is. If you are curious, I invite you to read the documentation on MSDN.
<bindings> <basicHttpBinding> <binding receiveTimeout="00:10:00" sendTimeout="00:10:00" openTimeout="00:10:00" closeTimeout="00:10:00" name="httpLargeMessageStream" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"> <readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" /> </binding> </basicHttpBinding> </bindings>
7- The service is ready to be published. Right-click on the FileUploadService project and choose Publish. Depending on your web hosting service, you need to set the info.
For GoDaddy:
– Use the FTP method.
– Set the target location. For me, I used ftp://ftp.ultimatepokermanager.com/upm/ServerTest
– Set the credentials
8- The trickiest part is to configure the application settings for the WCF service in the hosting service. In GoDaddy, it’s in the IIS Management in the Tools section. You need to check Anonymous Access and Set Application Root.
The upmservertest points to http://www.ultimatepokermanager.com/upm/servertest
8- To check if your service is running, open your browser and put the address of the web service. With my web service, the address is:
http://www.ultimatepokermanager.com/upm/servertest/FileUploader.svc
If you see something like the following image, you’re good to go!
B) Client side
I created a simple application that sends a string into a file in the server.
1- Create a Windows Phone Application. I assume that you know how to do this step.
2- The WCF service client FileUploader needs to be added to the project. Right-click on the Windows Phone project and click on Add Service Reference.
– Put the address of your WCF service. In this example, it is http://www.ultimatepokermanager.com/upm/servertest/FileUploader.svc
– Rename the namespace to FileUploader.
– Click OK.
3- Replace the content of the MainPage.xaml with:
<phone:PhoneApplicationPage x:Class="SendFileApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Margin="12,17,0,28"> <TextBlock Text="SEND FILE" Style="{StaticResource PhoneTextNormalStyle}" /> <TextBlock Text="dashboard" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}" /> </StackPanel> <StackPanel Grid.Row="1" Margin="12,0,12,0"> <TextBlock Margin="12,0,0,0" Text="Content" /> <TextBox Text="abcdefg" x:Name="textBoxContent" /> <TextBlock Margin="12,0,0,0" Text="File name" /> <TextBox Text="Sample.txt" x:Name="textBoxFileName" /> <Button Content="Send File" Click="ButtonSendFile" /> <Button Content="Download File" Click="ButtonDownloadFile" IsEnabled="False" x:Name="buttonDownloadFile"/> </StackPanel> </Grid> </phone:PhoneApplicationPage>
This content uses only standard controls.
4- Replace the content of MainPage.xaml.cs with:
using System; using System.Net; using System.Text; using System.Windows; using SendFileApp.FileUploader; namespace SendFileApp { public partial class MainPage { #region Constants // For this sample, the uploaded file can be access from my public folder private const string PublicServer = "http://www.ultimatepokermanager.com/upm/ServerTest/backup"; #endregion #region Fields private string _directory; #endregion #region Constructor public MainPage() { InitializeComponent(); } #endregion #region Event Handlers private void ButtonSendFile(object sender, RoutedEventArgs e) { byte[] content = Encoding.UTF8.GetBytes(textBoxContent.Text); _directory = Guid.NewGuid().ToString(); FileUploaderClient fileUploaderClient = new FileUploaderClient(); fileUploaderClient.UploadCompleted += UploadCompleted; fileUploaderClient.UploadAsync(new UploadFile { RelativeDirectory = string.Format("/{0}", _directory), FileName = textBoxFileName.Text, Content = content }); } private void UploadCompleted(object sender, UploadCompletedEventArgs uploadCompletedEventArgs) { buttonDownloadFile.IsEnabled = true; if (uploadCompletedEventArgs.Result == null) { MessageBox.Show("The file has been successfully uploaded.", "Success!", MessageBoxButton.OK); } else { MessageBox.Show(string.Format("An error occured:nn{0}", uploadCompletedEventArgs.Result), "Error", MessageBoxButton.OK); } } private void ButtonDownloadFile(object sender, RoutedEventArgs e) { WebClient webClient = new WebClient(); webClient.DownloadStringCompleted += WebClientDownloadStringCompleted; webClient.DownloadStringAsync(new Uri(string.Format("{0}/{1}/{2}", PublicServer, _directory, textBoxFileName.Text))); } private void WebClientDownloadStringCompleted(object sender, DownloadStringCompletedEventArgs downloadStringCompletedEventArgs) { if (downloadStringCompletedEventArgs.Error == null) { MessageBox.Show(string.Format("The content of your file is:nn{0}", downloadStringCompletedEventArgs.Result), "Success!", MessageBoxButton.OK); } else { MessageBox.Show(string.Format("An error occured:n{0}", downloadStringCompletedEventArgs.Error), "Error", MessageBoxButton.OK); } } #endregion } }
The interesting part in the previous code is:
private void ButtonSendFile(object sender, RoutedEventArgs e) { byte[] content = Encoding.UTF8.GetBytes(textBoxContent.Text); _directory = Guid.NewGuid().ToString(); FileUploaderClient fileUploaderClient = new FileUploaderClient(); fileUploaderClient.UploadCompleted += UploadCompleted; fileUploaderClient.UploadAsync(new UploadFile { RelativeDirectory = string.Format("/{0}", _directory), FileName = textBoxFileName.Text, Content = content }); }
The usage of the FileUploaderClient is really simple:
1- Create the instance FileUploaderClient.
2- Listen to the UploadCompleted event.
3- Upload the file asynchronously.
Please note that the UploadFile needs the properties RelativeDirectory (which starts with a “/”), a FileName and the Content in an array of bytes.
An example of an uploaded file address is: http://www.ultimatepokermanager.com/upm/ServerTest/backup/a5a8ae0b-50f0-4d16-90da-e83d6e1d30a3/Sample.txt. You can see the backup directory previously set in the Web.config file.
In the sample, I generated a GUID for the directory name. Also, the uploaded files are public as long as the user (or the application) knows about the directory name and the file name. If you want to add a layer of privacy, you just have to implement a DownloadFile method into your service. It should not be difficult if you read the code of the Upload method.
I guarantee you that you’ll be happy to see when your web service is working!
Download Sample project
Ken Cenerelli
Jul 27, 2012 @ 09:49:39
Sebastien – this is a cool little project. I will incorporate some the aspects into an app I am writing.
archiecoder
Jul 27, 2012 @ 10:40:15
Thank you for your comment! Let me know when your app will be live!
Jim
Aug 21, 2012 @ 11:26:08
How do the settings change if I already have a Godaddy account and IIS is already setup for the /blog to be the “Application Root”
and I want to have a similiar setup like yours upm/servertest
which I could store the uyploaded files.
What can be done here under Godaddy?
archiecoder
Aug 21, 2012 @ 17:35:29
Hello Jim,
I’m not sure that I understand your question.
If you have a folder on GoDaddy with the Anonymous Access and “Set Application Root”, you just have to deploy the service on that folder.
ArchieCoder
Jim
Aug 22, 2012 @ 08:19:26
Yes — Got it — It took GoDaddy very long to setup the new directory so my impatience caused my own problem.
I now have my blog (existing) and the new service and its associated directory to store files uploads working beautifully.
Thanks so much,
Jim
archiecoder
Aug 22, 2012 @ 08:40:06
I’m glad it is working for you!
Cheers
ArchieCoder
Branimir
Nov 20, 2013 @ 16:19:08
I modified this code to upload image as byte array and it crashes when it’s uploading. When I upload something smaller like a couple of integers, strings and booleans, it works fine. But with big byte array it crashes
archiecoder
Nov 20, 2013 @ 16:21:43
Hi,
It is a normal behavior. You can’t send like 1 MB of data in one shot. What you need to do is to split the array in smaller size and to reconstruct the file on the server. It is a whole new topic.
Good luck
ArchieCoder
Branimir
Nov 20, 2013 @ 16:49:03
What is the best way to upload image as byte array or base64string to server
Can you give me a short example, something like
[atributes]
public string Upload(byte[] image)
{
// it doesn’t have to do anything with image, I will do that
return result;
}
and in WP application something that can show upload progress and has stoping ability. maybe WebClient or TransferRequest