在 Azure 行動應用內,使用 Azure 儲存體
在 Azure 行動應用 綁定 Azure 儲存體
- 點選
資料連接,當出現Data Connections刀鋒頁面,點選+ Add連結 - 在
Add data conne...刀鋒頁面,點選標題Type欄位下方的下拉選單,選擇Storage- 接著點選下方的
Storage ma8be62c4718c34f > - 在
儲存體帳戶刀鋒視窗下,點選+ 新建 - 在 剛剛出現的
儲存體帳戶刀鋒視窗,請點選下方的確定按鈕
- 當回到
Add data conne...刀鋒視窗下方,點選確定按鈕 - 當 Data Connection 建立完成後,會在
Data Connections刀鋒視窗內,看到MS_AzureStorageAccountConnectionString名稱出現。
- 當回到 Azure 儀表板,點選
所有資源,在所有資源刀鋒視窗內,會看到這個ma8be62c4718c34f之Azure 儲存體已經建立完成了。

- 當回到 Azure 儀表板,點選
修改 Azure Mobile App後端服務專案
在這裡,將會使用完成 附件 Azure Mobile App 後端服務建立說明 實作內容後,得到的 Azure Mobile App後端服務專案 來繼續實作這個範例。
- 使用 Visual Studio 2015,開啟 Azure Mobile App後端服務專案 `DoggyMobileBE.sln
- 將這個 NuGet 套件加入
Microsoft.Azure.Mobile.Server.Files到專案內請記得要勾選包括搶鮮版
- 使用滑鼠右擊
DoggyMobileBEService>Controllers選擇加入>控制器 - 當出現
新增 Scaffold對話窗出現後,點選Web API 2 控制器 - 空白,然後點選新增按鈕
- 在
加入控制器對話窗內,把TodoItemStorageController填入到控制器名稱欄位內
- 當
TodoItemStorageController.cs檔案建立完成之後,請在該檔案內加入底下using敘述
using Microsoft.Azure.Mobile.Server.Files;
using Microsoft.Azure.Mobile.Server.Files.Controllers;
using DoggyMobileBEService.DataObjects;
using System.Threading.Tasks;
- 變更
StorageController類別的基礎類別為StorageController<TodoItem>
public class TodoItemStorageController : StorageController<TodoItem>
- 加入底下方法到這個類別內
[HttpPost]
[Route("tables/TodoItem/{id}/StorageToken")]
public async Task<HttpResponseMessage> PostStorageTokenRequest(string id, StorageTokenRequest value)
{
StorageToken token = await GetStorageTokenAsync(id, value);
return Request.CreateResponse(token);
}
// Get the files associated with this record
[HttpGet]
[Route("tables/TodoItem/{id}/MobileServiceFiles")]
public async Task<HttpResponseMessage> GetFiles(string id)
{
IEnumerable<MobileServiceFile> files = await GetRecordFilesAsync(id);
return Request.CreateResponse(files);
}
[HttpDelete]
[Route("tables/TodoItem/{id}/MobileServiceFiles/{name}")]
public Task Delete(string id, string name)
{
return base.DeleteFileAsync(id, name);
}
- 更新 Web API 的設定,請開啟
App_Start資料夾內的Startup.MobileApp.cs檔案;將下列程式碼加入到config變數定義之後。
config.MapHttpAttributeRoutes();
- 請重新發行這個專案,使用滑鼠右擊
DoggyMobileBEService,並選擇發行選項。
修正 Xamarin.Forms 可以使用 Azure 儲存體
- 使用 Visual Studio 2015,開啟 Azure Mobile App 前端專案
DoggyMobileBE.sln - 滑鼠右擊方案
DoggyMobileBE,選擇管理方案的 NuGet 套件,請依序安裝底下套件到所有專案內。請記得要勾選包括搶鮮版- Microsoft.Azure.Mobile.Client.Files
- Microsoft.Azure.Mobile.Client.SQLiteStore
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE,接著選擇加入>新增項目- 在
加入新項目 - DoggyMobileBE對話窗內,點選Visual C#>介面,在下方名稱欄位中,輸入IPlatform,接著,點選新增按鈕 - 在該檔案內,加入底下 using 敘述
using Microsoft.WindowsAzure.MobileServices.Files;
using Microsoft.WindowsAzure.MobileServices.Files.Metadata;
using Microsoft.WindowsAzure.MobileServices.Sync;
- 將該介面的定義使用底下程式碼替換
public interface IPlatform
{
Task <string> GetTodoFilesPathAsync();
Task<IMobileServiceFileDataSource> GetFileDataSource(MobileServiceFileMetadata metadata);
Task<string> TakePhotoAsync(object context);
Task DownloadFileAsync<T>(IMobileServiceSyncTable<T> table, MobileServiceFile file, string filename);
}
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE,接著選擇加入>類別- 在
加入新項目 - DoggyMobileBE對話窗內,點選Visual C#>類別,在下方名稱欄位中,輸入FileHelper,接著,點選新增按鈕 - 在該檔案內,加入底下 using 敘述
using System.IO;
using PCLStorage;
using System.Threading.Tasks;
using Xamarin.Forms;
* 將該類別的定義使用底下程式碼替換
public class FileHelper
{
public static async Task<string> CopyTodoItemFileAsync(string itemId, string filePath)
{
IFolder localStorage = FileSystem.Current.LocalStorage;
string fileName = Path.GetFileName(filePath);
string targetPath = await GetLocalFilePathAsync(itemId, fileName);
var sourceFile = await localStorage.GetFileAsync(filePath);
var sourceStream = await sourceFile.OpenAsync(FileAccess.Read);
var targetFile = await localStorage.CreateFileAsync(targetPath, CreationCollisionOption.ReplaceExisting);
using (var targetStream = await targetFile.OpenAsync(FileAccess.ReadAndWrite)) {
await sourceStream.CopyToAsync(targetStream);
}
return targetPath;
}
public static async Task<string> GetLocalFilePathAsync(string itemId, string fileName)
{
IPlatform platform = DependencyService.Get<IPlatform>();
string recordFilesPath = Path.Combine(await platform.GetTodoFilesPathAsync(), itemId);
var checkExists = await FileSystem.Current.LocalStorage.CheckExistsAsync(recordFilesPath);
if (checkExists == ExistenceCheckResult.NotFound) {
await FileSystem.Current.LocalStorage.CreateFolderAsync(recordFilesPath, CreationCollisionOption.ReplaceExisting);
}
return Path.Combine(recordFilesPath, fileName);
}
public static async Task DeleteLocalFileAsync(Microsoft.WindowsAzure.MobileServices.Files.MobileServiceFile fileName)
{
string localPath = await GetLocalFilePathAsync(fileName.ParentId, fileName.Name);
var checkExists = await FileSystem.Current.LocalStorage.CheckExistsAsync(localPath);
if (checkExists == ExistenceCheckResult.FileExists) {
var file = await FileSystem.Current.LocalStorage.GetFileAsync(localPath);
await file.DeleteAsync();
}
}
}
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE,接著選擇加入>類別- 在
加入新項目 - DoggyMobileBE對話窗內,點選Visual C#>類別,在下方名稱欄位中,輸入TodoItemFileSyncHandler,接著,點選新增按鈕 - 在該檔案內,加入底下 using 敘述
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices.Files.Sync;
using Microsoft.WindowsAzure.MobileServices.Files;
using Microsoft.WindowsAzure.MobileServices.Files.Metadata;
using Xamarin.Forms;
* 將該類別的定義使用底下程式碼替換
public class TodoItemFileSyncHandler : IFileSyncHandler
{
private readonly TodoItemManager todoItemManager;
public TodoItemFileSyncHandler(TodoItemManager itemManager)
{
this.todoItemManager = itemManager;
}
public Task<IMobileServiceFileDataSource> GetDataSource(MobileServiceFileMetadata metadata)
{
IPlatform platform = DependencyService.Get<IPlatform>();
return platform.GetFileDataSource(metadata);
}
public async Task ProcessFileSynchronizationAction(MobileServiceFile file, FileSynchronizationAction action)
{
if (action == FileSynchronizationAction.Delete) {
await FileHelper.DeleteLocalFileAsync(file);
}
else { // Create or update. We're aggressively downloading all files.
await this.todoItemManager.DownloadFileAsync(file);
}
}
}
- 展開核心PCL
DoggyMobileBE專案,滑鼠雙擊Properties節點- 當在 Visual Studio 2015 內,出現了
DoggyMobileBE視窗,請點選建置 - 在標題
條件式編譯的符號欄位內,填入OFFLINE_SYNC_ENABLED

- 在核心PCL 專案內,開啟
TodoItemManager.cs檔案,- 在該檔案內,加入底下 using 敘述
using System.IO;
using Xamarin.Forms;
using Microsoft.WindowsAzure.MobileServices.Files;
using Microsoft.WindowsAzure.MobileServices.Files.Sync;
using Microsoft.WindowsAzure.MobileServices.Eventing;
- 在
TodoItemManager類別建構式內,在DefineTable()方法之後,加入底下程式碼
// Initialize file sync
this.client.InitializeFileSyncContext(new TodoItemFileSyncHandler(this), store);
- 在建構式內,將
InitializeAsync方法使用底下程式碼置換
this.client.SyncContext.InitializeAsync(store, StoreTrackingOptions.NotifyLocalAndServerOperations);
- 在
SyncAsync()方法內,將底下程式碼加入到PushAsync()之後
await this.todoTable.PushFileChangesAsync();
- 加入底下方法到
TodoItemManager類別內
internal async Task DownloadFileAsync(MobileServiceFile file)
{
var todoItem = await todoTable.LookupAsync(file.ParentId);
IPlatform platform = DependencyService.Get<IPlatform>();
string filePath = await FileHelper.GetLocalFilePathAsync(file.ParentId, file.Name);
await platform.DownloadFileAsync(this.todoTable, file, filePath);
}
internal async Task<MobileServiceFile> AddImage(TodoItem todoItem, string imagePath)
{
string targetPath = await FileHelper.CopyTodoItemFileAsync(todoItem.Id, imagePath);
return await this.todoTable.AddFileAsync(todoItem, Path.GetFileName(targetPath));
}
internal async Task DeleteImage(TodoItem todoItem, MobileServiceFile file)
{
await this.todoTable.DeleteFileAsync(file);
}
internal async Task<IEnumerable<MobileServiceFile>> GetImageFilesAsync(TodoItem todoItem)
{
return await this.todoTable.GetFilesAsync(todoItem);
}
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE,接著選擇加入>類別- 在
加入新項目 - DoggyMobileBE對話窗內,點選Visual C#>類別,在下方名稱欄位中,輸入TodoItemImage,接著,點選新增按鈕 - 在該檔案內,使用底下類別定義替換掉 TodoItemImage 的定義
public class TodoItemImage : INotifyPropertyChanged
{
private string name;
private string uri;
public MobileServiceFile File { get; private set; }
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
public string Uri
{
get { return uri; }
set
{
uri = value;
OnPropertyChanged(nameof(Uri));
}
}
public TodoItemImage(MobileServiceFile file, TodoItem todoItem)
{
Name = file.Name;
File = file;
FileHelper.GetLocalFilePathAsync(todoItem.Id, file.Name).ContinueWith(x => this.Uri = x.Result);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
- 在核心PCL內,打開
App.cs- 將檔案內的
MainPage使用底下程式碼來替換
- 將檔案內的
MainPage = new NavigationPage(new TodoList());
- 在這個 App 類別內,加入底下靜態屬性
public static object UIContext { get; set; }
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE,接著選擇加入>新增項目- 在
加入新項目 - DoggyMobileBE對話窗內,點選Cross-Platform>Forms Xaml Page,在下方名稱欄位中,輸入TodoItemDetailsView,接著,點選新增按鈕 - 打開
TodoItemDetailsView.xaml檔案,使用底下定義替換掉這個檔案內的ContentPage內的宣告
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Clicked="OnAdd" Text="Add image"></Button>
<ListView x:Name="imagesList"
ItemsSource="{Binding Images}"
IsPullToRefreshEnabled="false"
Grid.Row="2">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell ImageSource="{Binding Uri}"
Text="{Binding Name}">
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
* 打開 `TodoItemDetailsView.xaml.cs` 檔案,將底下的 using 定義加入到這個檔案內
using System.Collections.ObjectModel;
using Microsoft.WindowsAzure.MobileServices.Files;
* 使用底下程式碼替換掉 `TodoItemDetailsView` 類別定義
public partial class TodoItemDetailsView : ContentPage
{
private TodoItemManager manager;
public TodoItem TodoItem { get; set; }
public ObservableCollection<TodoItemImage> Images { get; set; }
public TodoItemDetailsView(TodoItem todoItem, TodoItemManager manager)
{
InitializeComponent();
this.Title = todoItem.Name;
this.TodoItem = todoItem;
this.manager = manager;
this.Images = new ObservableCollection<TodoItemImage>();
this.BindingContext = this;
}
public async Task LoadImagesAsync()
{
IEnumerable<MobileServiceFile> files = await this.manager.GetImageFilesAsync(TodoItem);
this.Images.Clear();
foreach (var f in files) {
var todoImage = new TodoItemImage(f, this.TodoItem);
this.Images.Add(todoImage);
}
}
public async void OnAdd(object sender, EventArgs e)
{
IPlatform mediaProvider = DependencyService.Get<IPlatform>();
string sourceImagePath = await mediaProvider.TakePhotoAsync(App.UIContext);
if (sourceImagePath != null) {
MobileServiceFile file = await this.manager.AddImage(this.TodoItem, sourceImagePath);
var image = new TodoItemImage(file, this.TodoItem);
this.Images.Add(image);
}
}
}
- 在核心PCL 專案內,打開
TodoList.xaml.cs檔案- 請將
OnSelected方法,使用底下程式碼替換掉
- 請將
public async void OnSelected(object sender, SelectedItemChangedEventArgs e)
{
var todo = e.SelectedItem as TodoItem;
if (todo != null) {
var detailsView = new TodoItemDetailsView(todo, manager);
await detailsView.LoadImagesAsync();
await Navigation.PushAsync(detailsView);
}
todoList.SelectedItem = null;
}
修正 DoggyMobileBE.Droid 專案內容
- 在 DoggyMobileBE.Droid 專案,滑鼠右擊專案內的
Components節點,接著,選擇Get More Components...- 當出現
All Components對話視窗,請在左上方輸入Xamarin.Mobile,進行搜尋這個元件,當搜尋到之後,點選右半部的那個元件。

- 當
Xamarin.Mobile元件出現之後,請點選Add to App按鈕,安裝這個元件到 Android 專案內。

- 當出現
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE.Droid,接著選擇加入>類別- 在
加入新項目 - DoggyMobileBE.Droid對話窗內,點選Visual C#>Class,在下方名稱欄位中,輸入DroidPlatform,接著,點選新增按鈕 - 在該檔案內,使用底下類別定義替換掉 TodoItemImage 的定義
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices.Sync;
using Microsoft.WindowsAzure.MobileServices.Files.Metadata;
using Microsoft.WindowsAzure.MobileServices.Files;
using Microsoft.WindowsAzure.MobileServices.Files.Sync;
using System.IO;
using Xamarin.Media;
[assembly: Xamarin.Forms.Dependency(typeof(DoggyMobileBE.Droid.DroidPlatform))]
namespace DoggyMobileBE.Droid
{
public class DroidPlatform : IPlatform
{
public async Task DownloadFileAsync<T>(IMobileServiceSyncTable<T> table, MobileServiceFile file, string filename)
{
var path = await FileHelper.GetLocalFilePathAsync(file.ParentId, file.Name);
await table.DownloadFileAsync(file, path);
}
public async Task<IMobileServiceFileDataSource> GetFileDataSource(MobileServiceFileMetadata metadata)
{
var filePath = await FileHelper.GetLocalFilePathAsync(metadata.ParentDataItemId, metadata.FileName);
return new PathMobileServiceFileDataSource(filePath);
}
public async Task<string> TakePhotoAsync(object context)
{
try
{
var uiContext = context as Context;
if (uiContext != null)
{
var mediaPicker = new MediaPicker(uiContext);
var photo = await mediaPicker.TakePhotoAsync(new StoreCameraMediaOptions());
return photo.Path;
}
}
catch (TaskCanceledException)
{
}
return null;
}
public Task<string> GetTodoFilesPathAsync()
{
string appData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
string filesPath = Path.Combine(appData, "TodoItemFiles");
if (!Directory.Exists(filesPath))
{
Directory.CreateDirectory(filesPath);
}
return Task.FromResult(filesPath);
}
}
}
- 打開
MainActivity.cs檔案,在OnCreate方法內,在LoadApplication()方法之前,加入底下程式碼。
App.UIContext = this;
- 滑鼠右擊專案節點
DoggyMobileBE.Droid選擇設定為起始專案,並且按下 F5 開始執行- 因為採用離線存取方式,因此,就算當時裝置沒有連上網路,也可以看的到資料。

- 使用 Add image 按鈕,可以透過裝置新增照片,並且該這片將會儲存到遠端的 Azure 儲存體中

- 另外,在 Azure 的
Blob服務網頁中,也可以看到這些圖片已經儲存到 Azure 儲存體上了。
修正 DoggyMobileBE.iOS 專案內容
- 在 DoggyMobileBE.iOS 專案,滑鼠右擊專案內的
Components節點,接著,選擇Get More Components...- 當出現
All Components對話視窗,請在左上方輸入Xamarin.Mobile,進行搜尋這個元件,當搜尋到之後,點選右半部的那個元件。

- 當
Xamarin.Mobile元件出現之後,請點選Add to App按鈕,安裝這個元件到 Android 專案內。

- 當出現
- 在核心PCL專案,滑鼠右擊節點
DoggyMobileBE.Droid,接著選擇加入>類別- 在
加入新項目 - DoggyMobileBE.Droid對話窗內,點選Apple>類別,在下方名稱欄位中,輸入TouchPlatform,接著,點選新增按鈕 - 在該檔案內,使用底下類別定義替換掉 TodoItemImage 的定義
using Microsoft.WindowsAzure.MobileServices.Files;
using Microsoft.WindowsAzure.MobileServices.Files.Metadata;
using Microsoft.WindowsAzure.MobileServices.Files.Sync;
using Microsoft.WindowsAzure.MobileServices.Sync;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Media;
[assembly: Xamarin.Forms.Dependency(typeof(DoggyMobileBE.iOS.TouchPlatform))]
namespace DoggyMobileBE.iOS
{
class TouchPlatform : IPlatform
{
public async Task DownloadFileAsync<T>(IMobileServiceSyncTable<T> table, MobileServiceFile file, string filename)
{
var path = await FileHelper.GetLocalFilePathAsync(file.ParentId, file.Name);
await table.DownloadFileAsync(file, path);
}
public async Task<IMobileServiceFileDataSource> GetFileDataSource(MobileServiceFileMetadata metadata)
{
var filePath = await FileHelper.GetLocalFilePathAsync(metadata.ParentDataItemId, metadata.FileName);
return new PathMobileServiceFileDataSource(filePath);
}
public async Task<string> TakePhotoAsync(object context)
{
try
{
var mediaPicker = new MediaPicker();
var mediaFile = await mediaPicker.PickPhotoAsync();
return mediaFile.Path;
}
catch (TaskCanceledException)
{
return null;
}
}
public Task<string> GetTodoFilesPathAsync()
{
string filesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "TodoItemFiles");
if (!Directory.Exists(filesPath))
{
Directory.CreateDirectory(filesPath);
}
return Task.FromResult(filesPath);
}
}
}
- 打開
AppDelegate.cs檔案,解除的註解SQLitePCL.CurrentPlatform.Init()。
App.UIContext = this;
- 滑鼠右擊專案節點
DoggyMobileBE.Droid選擇設定為起始專案,並且按下 F5 開始執行- 因為採用離線存取方式,因此,就算當時裝置沒有連上網路,也可以看的到資料。

- 剛剛在 Android 平台下,已經在 Second item 項目內新增了三個照片,所以,當開啟 iOS 應用程式的時候,就可以直接看到這些圖片了。也可以從圖檔編號是一樣來確認。

doggymobilebe 行動 App圖示,進入到doggymobilebe 行動 App設定刀鋒頁面。