Partager via


Uploading Images from your Windows Phone to Azure Container and Retrieving the Images in a List

Today we are going to look into how we can select image from our windows phone device and upload them to azure. There are basically two steps involved in it,

  1. Selecting the Images using FileOpenPicker
  2. Creating a container on Azure and uploading the file and get it back

First we are going to use FileOpenPicker to select a file and Continue to our Application,

Selecting a file on windows Phone was pretty straight forward and to the point, but with winRT it has been changed and requires quit a work to get it done.

When you call a file picker from a Windows Phone Store app, your app is deactivated until the file picker returns the selection made by the user. On low-memory phones, however, your app may be terminated. Because of this possibility, you have to call different methods in a Windows Phone Store app than you call in a Windows Store app to continue your app after a file picker operation. The following table shows these methods.

Task

Methods to call from a windows Store App

Methods to call from a Windows Phone App

Pick a file to Open

PickSingleFileAsync

PickSingleFineAndContinue

Pick a location and filename to save a file

PickSaveFileAsync

PickSaveFileAndContinue

Pick a Folder

PickSingleFolderAsync

PickFolderAndContinue

To Call a FileOpenPicker and Continue your App

  1. Include the SuspensionManager helper class in your project. This class simplifies lifecycle management for your app. To get the SuspensionManager helper class, create a new Windows Phone app that uses a project template other than the Blank App template. The SuspensionManager.cs file is in the Common folder of the project.
  2. Include a helper class like the custom ContinuationManager helper class in your project. This class includes interfaces and methods that make it easier to continue your app. To get the code for the ContinuationManager helper class, download the sample project attached, or see How to continue your Windows Phone app after calling an AndContinue method.
  3. We are going to use Completion Directives to determine which part of the code would execute on which platform, because we are targeting WinRT, therefore the same code can be used on Windows Store apps as well.

 

The Compelte Code for ContinuationManager Class and App.Xaml.cs is written below,

APP.XAML.CS

 public sealed partial class App : Application
 {
 #if WINDOWS_PHONE_APP
 ContinuationManager continuationManager;
 #endif
 private TransitionCollection transitions;
 
 
 /// <summary>
 /// Initializes the singleton application object. This is the first line of authored code
 /// executed, and as such is the logical equivalent of main() or WinMain().
 /// </summary>
 public App()
 {
 this.InitializeComponent();
 this.Suspending += this.OnSuspending;
 }
 
 /// <summary>
 /// Invoked when the application is launched normally by the end user. Other entry points
 /// will be used when the application is launched to open a specific file, to display
 /// search results, and so forth.
 /// </summary>
 /// <param name="e">Details about the launch request and process.</param>
 /// 
 
 
 private Frame CreateRootFrame()
 {
 Frame rootFrame = Window.Current.Content as Frame;
 
 // Do not repeat app initialization when the Window already has content,
 // just ensure that the window is active
 if (rootFrame == null)
 {
 // Create a Frame to act as the navigation context and navigate to the first page
 rootFrame = new Frame();
 
 // Set the default language
 rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
 rootFrame.NavigationFailed += OnNavigationFailed;
 
 // Place the frame in the current Window
 Window.Current.Content = rootFrame;
 }
 
 return rootFrame;
 }
 
 private async Task RestoreStatusAsync(ApplicationExecutionState previousExecutionState)
 {
 // Do not repeat app initialization when the Window already has content,
 // just ensure that the window is active
 if (previousExecutionState == ApplicationExecutionState.Terminated)
 {
 // Restore the saved session state only when appropriate
 try
 {
 await SuspensionManager.RestoreAsync();
 }
 catch (SuspensionManagerException)
 {
 //Something went wrong restoring state.
 //Assume there is no state and continue
 }
 }
 }
 
 #if WINDOWS_PHONE_APP
 /// <summary>
 /// Handle OnActivated event to deal with File Open/Save continuation activation kinds
 /// </summary>
 /// <param name="e">Application activated event arguments, it can be casted to proper sub-type based on ActivationKind</param>
 protected async override void OnActivated(IActivatedEventArgs e)
 {
 base.OnActivated(e);
 
 continuationManager = new ContinuationManager();
 
 Frame rootFrame = CreateRootFrame();
 await RestoreStatusAsync(e.PreviousExecutionState);
 
 if (rootFrame.Content == null)
 {
 rootFrame.Navigate(typeof(MainPage));
 }
 
 var continuationEventArgs = e as IContinuationActivatedEventArgs;
 if (continuationEventArgs != null)
 //{
 // Frame scenarioFrame = MainPage.Current.FindName("ScenarioFrame") as Frame;
 // if (scenarioFrame != null)
 // {
 // // Call ContinuationManager to handle continuation activation
 // continuationManager.Continue(continuationEventArgs, scenarioFrame);
 // }
 //}
 continuationManager.Continue(continuationEventArgs, rootFrame);
 Window.Current.Activate();
 }
 #endif
 protected override void OnLaunched(LaunchActivatedEventArgs e)
 {
 #if DEBUG
 if (System.Diagnostics.Debugger.IsAttached)
 {
 this.DebugSettings.EnableFrameRateCounter = true;
 }
 #endif
 
 Frame rootFrame = Window.Current.Content as Frame;
 
 // Do not repeat app initialization when the Window already has content,
 // just ensure that the window is active
 if (rootFrame == null)
 {
 // Create a Frame to act as the navigation context and navigate to the first page
 rootFrame = new Frame();
 
 // TODO: change this value to a cache size that is appropriate for your application
 rootFrame.CacheSize = 1;
 
 if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
 {
 // TODO: Load state from previously suspended application
 }
 
 // Place the frame in the current Window
 Window.Current.Content = rootFrame;
 }
 
 if (rootFrame.Content == null)
 {
 // Removes the turnstile navigation for startup.
 if (rootFrame.ContentTransitions != null)
 {
 this.transitions = new TransitionCollection();
 foreach (var c in rootFrame.ContentTransitions)
 {
 this.transitions.Add(c);
 }
 }
 
 rootFrame.ContentTransitions = null;
 rootFrame.Navigated += this.RootFrame_FirstNavigated;
 
 // When the navigation stack isn't restored navigate to the first page,
 // configuring the new page by passing required information as a navigation
 // parameter
 if (!rootFrame.Navigate(typeof(MainPage), e.Arguments))
 {
 throw new Exception("Failed to create initial page");
 }
 }
 
 // Ensure the current window is active
 Window.Current.Activate();
 }
 
 void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
 {
 throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
 }
 
 /// <summary>
 /// Restores the content transitions after the app has launched.
 /// </summary>
 /// <param name="sender">The object where the handler is attached.</param>
 /// <param name="e">Details about the navigation event.</param>
 private void RootFrame_FirstNavigated(object sender, NavigationEventArgs e)
 {
 var rootFrame = sender as Frame;
 rootFrame.ContentTransitions = this.transitions ?? new TransitionCollection() { new NavigationThemeTransition() };
 rootFrame.Navigated -= this.RootFrame_FirstNavigated;
 }
 
 /// <summary>
 /// Invoked when application execution is being suspended. Application state is saved
 /// without knowing whether the application will be terminated or resumed with the contents
 /// of memory still intact.
 /// </summary>
 /// <param name="sender">The source of the suspend request.</param>
 /// <param name="e">Details about the suspend request.</param>
 private async void OnSuspending(object sender, SuspendingEventArgs e)
 {
 var deferral = e.SuspendingOperation.GetDeferral();
 await SuspensionManager.SaveAsync();
 // TODO: Save application state and stop any background activity
 deferral.Complete();
 }
 }
 CONTINUATIONMANAGER.CS
 #if WINDOWS_PHONE_APP
 /// <summary>
 /// ContinuationManager is used to detect if the most recent activation was due
 /// to a continuation such as the FileOpenPicker or WebAuthenticationBroker
 /// </summary>
 public class ContinuationManager
 {
 IContinuationActivatedEventArgs args = null;
 bool handled = false;
 Guid id = Guid.Empty;
 
 /// <summary>
 /// Sets the ContinuationArgs for this instance. Using default Frame of current Window
 /// Should be called by the main activation handling code in App.xaml.cs
 /// </summary>
 /// <param name="args">The activation args</param>
 internal void Continue(IContinuationActivatedEventArgs args)
 {
 Continue(args, Window.Current.Content as Frame);
 }
 
 /// <summary>
 /// Sets the ContinuationArgs for this instance. Should be called by the main activation
 /// handling code in App.xaml.cs
 /// </summary>
 /// <param name="args">The activation args</param>
 /// <param name="rootFrame">The frame control that contains the current page</param>
 internal void Continue(IContinuationActivatedEventArgs args, Frame rootFrame)
 {
 if (args == null)
 throw new ArgumentNullException("args");
 
 if (this.args != null && !handled)
 throw new InvalidOperationException("Can't set args more than once");
 
 this.args = args;
 this.handled = false;
 this.id = Guid.NewGuid();
 
 if (rootFrame == null)
 return;
 
 switch (args.Kind)
 {
 case ActivationKind.PickFileContinuation:
 var fileOpenPickerPage = rootFrame.Content as IFileOpenPickerContinuable;
 if (fileOpenPickerPage != null)
 {
 fileOpenPickerPage.ContinueFileOpenPicker(args as FileOpenPickerContinuationEventArgs);
 }
 break;
 
 case ActivationKind.PickSaveFileContinuation:
 var fileSavePickerPage = rootFrame.Content as IFileSavePickerContinuable;
 if (fileSavePickerPage != null)
 {
 fileSavePickerPage.ContinueFileSavePicker(args as FileSavePickerContinuationEventArgs);
 }
 break;
 
 case ActivationKind.PickFolderContinuation:
 var folderPickerPage = rootFrame.Content as IFolderPickerContinuable;
 if (folderPickerPage != null)
 {
 folderPickerPage.ContinueFolderPicker(args as FolderPickerContinuationEventArgs);
 }
 break;
 
 case ActivationKind.WebAuthenticationBrokerContinuation:
 var wabPage = rootFrame.Content as IWebAuthenticationContinuable;
 if (wabPage != null)
 {
 wabPage.ContinueWebAuthentication(args as WebAuthenticationBrokerContinuationEventArgs);
 }
 break;
 }
 }
 
 /// <summary>
 /// Marks the contination data as 'stale', meaning that it is probably no longer of
 /// any use. Called when the app is suspended (to ensure future activations don't appear
 /// to be for the same continuation) and whenever the continuation data is retrieved 
 /// (so that it isn't retrieved on subsequent navigations)
 /// </summary>
 internal void MarkAsStale()
 {
 this.handled = true;
 }
 
 /// <summary>
 /// Retrieves the continuation args, if they have not already been retrieved, and 
 /// prevents further retrieval via this property (to avoid accidentla double-usage)
 /// </summary>
 public IContinuationActivatedEventArgs ContinuationArgs
 {
 get
 {
 if (handled)
 return null;
 MarkAsStale();
 return args;
 }
 }
 
 /// <summary>
 /// Unique identifier for this particular continuation. Most useful for components that 
 /// retrieve the continuation data via <see cref="GetContinuationArgs"/> and need
 /// to perform their own replay check
 /// </summary>
 public Guid Id { get { return id; } }
 
 /// <summary>
 /// Retrieves the continuation args, optionally retrieving them even if they have already
 /// been retrieved
 /// </summary>
 /// <param name="includeStaleArgs">Set to true to return args even if they have previously been returned</param>
 /// <returns>The continuation args, or null if there aren't any</returns>
 public IContinuationActivatedEventArgs GetContinuationArgs(bool includeStaleArgs)
 {
 if (!includeStaleArgs && handled)
 return null;
 MarkAsStale();
 return args;
 }
 }
 
 /// <summary>
 /// Implement this interface if your page invokes the file open picker
 /// API.
 /// </summary>
 interface IFileOpenPickerContinuable
 {
 /// <summary>
 /// This method is invoked when the file open picker returns picked
 /// files
 /// </summary>
 /// <param name="args">Activated event args object that contains returned files from file open picker</param>
 void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args);
 }
 
 /// <summary>
 /// Implement this interface if your page invokes the file save picker
 /// API
 /// </summary>
 interface IFileSavePickerContinuable
 {
 /// <summary>
 /// This method is invoked when the file save picker returns saved
 /// files
 /// </summary>
 /// <param name="args">Activated event args object that contains returned file from file save picker</param>
 void ContinueFileSavePicker(FileSavePickerContinuationEventArgs args);
 }
 
 /// <summary>
 /// Implement this interface if your page invokes the folder picker API
 /// </summary>
 interface IFolderPickerContinuable
 {
 /// <summary>
 /// This method is invoked when the folder picker returns the picked
 /// folder
 /// </summary>
 /// <param name="args">Activated event args object that contains returned folder from folder picker</param>
 void ContinueFolderPicker(FolderPickerContinuationEventArgs args);
 }
 
 /// <summary>
 /// Implement this interface if your page invokes the web authentication
 /// broker
 /// </summary>
 interface IWebAuthenticationContinuable
 {
 /// <summary>
 /// This method is invoked when the web authentication broker returns
 /// with the authentication result
 /// </summary>
 /// <param name="args">Activated event args object that contains returned authentication token</param>
 void ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args);
 }
 
 #endif

Now, open MainPage.xaml, and write down the following Xaml, we are going to use a button to select a file and a ListBox to Display the Images that are being uploaded to Azure

 <StackPanel Margin="20">
 <ProgressBar IsIndeterminate="True" Visibility="Collapsed" Name="Progressbar1"></ProgressBar>
 <Button Content="select and Upload File" Click="Button_Click"/>
 <ListBox Name="lstBlobs">
 <ListBox.ItemTemplate>
 <DataTemplate>
 <Image Width="100" Height="100" Source="{Binding Uri}"/>
 </DataTemplate>
 </ListBox.ItemTemplate>
 </ListBox>
 </StackPanel>

First and foremost, extend The MainPage from Page, IFileOpenPickerContinuable

On the Button Click event, write down the following code

 FileOpenPicker picker = new FileOpenPicker();
 picker.ViewMode = PickerViewMode.Thumbnail;
 picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
 
 picker.FileTypeFilter.Add(".Jpeg");
 picker.FileTypeFilter.Add(".Jpg");
 picker.FileTypeFilter.Add(".png");
 picker.PickSingleFileAndContinue();
 Create a method named ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args) and write the following code
 {
 if (args.Files.Count > 0)
 {
 UploadFile(args.Files[0]);
 }
 }

For now, do not bother about what UploadFile method do, we are going to explain it in a while, 

Uptill now, we are done with the FileOpenPicker for Windows Phone, so if you have a project that only displays an Image from your picture library, then that’s about it, you can easily set the source of an Image in the ContinueFileOpenPicker Method, but we are going to go a step ahead and move these Images to Azure Container and would retrieve them in a list.

Uploading the Images to Azure Container

Uploading files to Azure requires an Active Azure Account, if you have BizSpark Subscription, you can use Azure provided in that. Assuming that you have an Active Azure Account, Logon to the Azure Portal by using the Azure Portal.

Once there, navigate to storage section on the left menu,

 

 

In the Storage Section, click the New Button

  

Click, New and Click Quick Create

Give the Storage a Unique name, and specify your preffered region, in the Replication Section select Geo-Redundant, for more on Storage Redundancy Options click here.

Once Created, Click on the name you mentioned for the storage

In the storage section, click on the Containers section

In Containers, click Add Button to add a new Container, a popup windows will appear

Make sure the name is in lowercase and the Access level is public blob, otherwise you will not be able to see the Images.

That’s about it for Azure Portal, now head back to Visual Studio in your Solution and right click references in the Solution Explorer, and select manage Nuget Packages.

 

In the immediate windows, Search for WindowsAzure.Storage

Or, goto Tools > Nuget Package Manager > Package Manager Colsole

And write down the following statement.

Please note that we are using version 4.3.0, and it if you are using older versions of the SDK, the code might not work.

Once the package is installed, open MainPage.xaml.cs and add following nameSpaces at the Top

  
  using Microsoft.WindowsAzure.Storage;
 using Microsoft.WindowsAzure.Storage.Auth;
 using Microsoft.WindowsAzure.Storage.Blob;
 Above the Constructor, write down the following Code
 StorageCredentials creds;
 CloudStorageAccount _CloudStorageAccount;
 CloudBlobClient blobclient;
 CloudBlobContainer _cloudBlobContainer;

Now we are going to create a method that is going to access the storage Account. For that we need to the Storage Account Name and its primary Access Key, we can get this from the azure portal.
In the Portal, select the Storage Account 

And click Manage Access Keys from the buttons below

On the popup window, you can get the Name of the storage account and its primary and secondary access keys

Head back to Visual Studio and write down the following code

 async void GetBlobReferences()
 {
 string accountName = "your Storage Account Name";
 string accountKey = "Your Primary Access Key";
 
 try
 {
 creds = new StorageCredentials(accountName, accountKey);
 _CloudStorageAccount = new CloudStorageAccount(creds, useHttps: true);
 blobclient = _CloudStorageAccount.CreateCloudBlobClient();
 _cloudBlobContainer = blobclient.GetContainerReference("images");
 await _cloudBlobContainer.CreateIfNotExistsAsync();
 }
 catch (Exception)
 {
 throw;
 }
 }

In the above code we are providing the accountName and Account Key to StorageCredentials that represents a Cloud storage Account.

Call this method in the constructor.
Now we are going to create the UploadFile Method for our Images to be uploaded the Container

 private async void UploadFile(StorageFile file)
 {
 CloudBlockBlob blob = _cloudBlobContainer.GetBlockBlobReference(file.Name);
 await blob.UploadFromFileAsync(file);
 ListImages();
 }

In the End we are going to write down ListImages method that is going to retrieve the Images and will bind those Images to List ItemSource.

 private async void ListImages()
 {
 try
 {
 Progressbar1.Visibility = Windows.UI.Xaml.Visibility.Visible;
 lstBlobs.ItemsSource = (await _cloudBlobContainer.ListBlobsSegmentedAsync(null, true, BlobListingDetails.All, 20, null, null, null)).Results;
 }
 catch (Exception)
 {
 
 }
 finally
 {
 Progressbar1.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
 }
 }
 Call this method from the Constructor as well.

Thats about it we are good to go with our code.
 
 
 
  
 
 
 
 

AzureStorageImageUploader.rar

Comments

  • Anonymous
    March 04, 2015
    Awesome Indeed! Thanks for this best stuff !

  • Anonymous
    March 08, 2015
    very helpful tutorial ! thanks...

  • Anonymous
    March 09, 2015
    desperately needed,,, very helpful

  • Anonymous
    March 26, 2015
    THANK YOU SIR..!!! it will surely resolve my problem :)