與眾不同 windows phone (13) - Background Task(后台任務)之后台文件傳輸(上傳和下載)
作者:webabcd
介紹
與眾不同 windows phone 7.5 (sdk 7.1) 之后台任務
- 后台文件傳輸(下載)
- 后台文件傳輸(上傳)
示例
1、演示如何實現后台文件傳輸(下載)
BackgroundTransferDownload.xaml
<phone:PhoneApplicationPage x:Class="Demo.BackgroundTask.BackgroundTransferDownload" 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" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <Grid x:Name="LayoutRoot" Background="Transparent"> <StackPanel Orientation="Vertical"> <Button x:Name="btnDownloadSilverlightDemo" Content="下載 Silverlight Demo 壓縮包" Click="btnDownloadSilverlightDemo_Click" /> <Button x:Name="btnDownloadXNADemo" Content="下載 XNA Demo 壓縮包" Click="btnDownloadXNADemo_Click" /> <Button x:Name="btnDownloadWindowsPhoneDemo" Content="下載 WindowsPhone Demo 壓縮包" Click="btnDownloadWindowsPhoneDemo_Click" /> <TextBlock Text="下載任務列表:" Margin="0 15 0 0" /> <ListBox Name="listBox"> <ListBox.ItemTemplate> <DataTemplate> <Grid Margin="0 15" Width="480"> <Grid.ColumnDefinitions> <ColumnDefinition Width="8*"/> <ColumnDefinition Width="2*"/> </Grid.ColumnDefinitions> <Grid> <StackPanel Orientation="Vertical"> <StackPanel Orientation="Horizontal"> <TextBlock Text="文件名: "/> <TextBlock Text="{Binding Tag}"/> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="下載狀態: "/> <TextBlock Text="{Binding TransferStatus}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="已接收字節數: "/> <TextBlock Text="{Binding BytesReceived}" /> </StackPanel> <StackPanel Orientation="Horizontal"> <TextBlock Text="總共字節數: "/> <TextBlock Text="{Binding TotalBytesToReceive}" /> </StackPanel> </StackPanel> </Grid> <Grid Grid.Column="1"> <Button x:Name="btnCancel" Tag="{Binding RequestId}" Click="btnCancel_Click" Content="刪除任務"></Button> </Grid> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> </Grid> </phone:PhoneApplicationPage>
BackgroundTransferDownload.xaml.cs
/* * 演示如何實現后台文件傳輸(下載) * * * BackgroundTransferRequest - 后台文件傳輸請求 * BackgroundTransferRequest(Uri) - 構造函數。參數解釋:下載時其為需要下載的文件的遠程地址;上傳時其為接收文件的遠程服務地址 * RequestId - 后台文件傳輸請求的唯一 ID,只讀 * RequestUri - 請求的地址,只讀。其需要在構造函數中設置 * Method - 請求的 http 方法,GET - 下載;POST - 上傳 * Headers - 請求的 http 頭,有一些頭信息不能自定義,如 Range 等 * StatusCode - http 請求回應的狀態碼 * Tag - 自定義此對象的關聯信息,即上下文數據 * TransferPreferences - 傳輸的啟動條件(Microsoft.Phone.BackgroundTransfer.TransferPreferences 枚舉) * None - 僅在外部電源 + WiFi 環境下允許傳輸 * AllowCellular - 允許在手機網絡環境下傳輸 * AllowBattery - 允許在電池做電源的環境下傳輸 * AllowCellularAndBattery - 允許在電池電源 + 手機網絡的環境下傳輸 * TransferError - 錯誤信息 * TransferStatus - 傳輸狀態(Microsoft.Phone.BackgroundTransfer.TransferStatus 枚舉) * None - 傳輸任務尚未加入到傳輸隊列 * Waiting - 在傳輸隊列,等待傳輸 * Transferring - 傳輸中 * Paused - 暫停 * Completed - 傳輸任務已完成,如果傳輸成功則 TransferError 為 null * WaitingForWiFi - WiFi 網絡 * WaitingForExternalPower - 需要接上外接電源 * WaitingForExternalPowerDueToBatterySaverMode - 需要接上外接電源,或者禁用節電模式 * WaitingForNonVoiceBlockingNetwork - 不能使用無法和語音並發的網絡(如 2G、EDGE 和標准 GPRS) * DownloadLocation - 下載到的目標位置 * UploadLocation - 需要上傳的文件的地址 * BytesReceived - 文件已下載的字節數 * BytesSent - 文件已上傳的字節數 * TotalBytesToReceive - 需要下載的文件的總字節數(-1 則文件大小未知) * TotalBytesToSend - 需要上傳的文件的總字節數(-1 則文件大小未知) * * TransferProgressChanged - 傳輸進度改變時所觸發的事件 * TransferStatusChanged - 屬性 TransferStatus 發生改變時所觸發的事件 * * * BackgroundTransferService - 對后台文件傳輸請求的管理 * Add() - 添加一個 BackgroundTransferRequest * Find() - 通過 RequestId 查找指定的 BackgroundTransferRequest * Remove - 通過 RequestId 刪除指定的 BackgroundTransferRequest * * * 注意: * 后台文件傳輸既支持 http 又支持 https * 傳輸的文件必須在 /shared/transfers 目錄下 * 上傳文件最大 5 MB,手機網絡下載文件最大 20 MB,WiFi+電池電源下載文件最大 100 MB,WiFi+外接電源下載文件則允許大於 100 MB * 下載文件超過 5 MB 時,系統會向服務端發 Range 頭分段下載(不會多線程下載,但是會斷點續傳) * 每個應用程序的傳輸任務隊列,最大支持 5 個任務,設備支持 500 個任務 * 設備上最大並發傳輸數為 2 個 */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Phone.BackgroundTransfer; using System.IO.IsolatedStorage; namespace Demo.BackgroundTask { public partial class BackgroundTransferDownload : PhoneApplicationPage { public BackgroundTransferDownload() { InitializeComponent(); } protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { // 為每個 BackgroundTransferRequest 注冊相關事件 foreach (var transfer in BackgroundTransferService.Requests) { transfer.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged); transfer.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged); } UpdateUI(); } private void ProcessTransfer(BackgroundTransferRequest transfer) { switch (transfer.TransferStatus) { case TransferStatus.Completed: // 200 - 下載完成;206 - 分塊下載完成 if (transfer.StatusCode == 200 || transfer.StatusCode == 206) { // 下載完成后不會自動從隊列中刪除,需要手動刪除 RemoveTransferRequest(transfer.RequestId); // 將下載完成后的文件移動到獨立存儲的根目錄下 using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication()) { string filename = transfer.Tag; if (isoStore.FileExists(filename)) { isoStore.DeleteFile(filename); } isoStore.MoveFile(transfer.DownloadLocation.OriginalString, filename); } } else { // 如果回應的不是 200 或 206 則意味着出現了問題 // 將任務其移出隊列 RemoveTransferRequest(transfer.RequestId); // 發生了異常,做一些相關處理 if (transfer.TransferError != null) { MessageBox.Show(transfer.TransferError.ToString()); } } break; // 注:以下 4 個狀態最好做一下判斷和處理,然后呈現給用戶 case TransferStatus.WaitingForExternalPower: break; case TransferStatus.WaitingForExternalPowerDueToBatterySaverMode: break; case TransferStatus.WaitingForNonVoiceBlockingNetwork: break; case TransferStatus.WaitingForWiFi: break; } } void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e) { ProcessTransfer(e.Request); UpdateUI(); } void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e) { UpdateUI(); } private void btnCancel_Click(object sender, EventArgs e) { string transferId = ((Button)sender).Tag as string; RemoveTransferRequest(transferId); UpdateUI(); } // 從 BackgroundTransferService 中刪除指定的傳輸請求 private void RemoveTransferRequest(string transferId) { BackgroundTransferRequest transfer = BackgroundTransferService.Find(transferId); try { if (transfer != null) BackgroundTransferService.Remove(transfer); } catch (Exception ex) { MessageBox.Show("RemoveTransferRequest: " + ex.ToString()); } } private void UpdateUI() { // 更新 UI listBox.ItemsSource = BackgroundTransferService.Requests; } private void btnDownloadSilverlightDemo_Click(object sender, RoutedEventArgs e) { // 如果 url 中有特殊字符,如漢字等,建議使用 Uri.EscapeUriString 編碼 AddBackgroundTransfer("Silverlight.rar", new Uri("http://files.cnblogs.com/webabcd/Silverlight.rar", UriKind.Absolute)); UpdateUI(); } private void btnDownloadXNADemo_Click(object sender, RoutedEventArgs e) { AddBackgroundTransfer("XNA.rar", new Uri("http://files.cnblogs.com/webabcd/XNA.rar", UriKind.Absolute)); UpdateUI(); } private void btnDownloadWindowsPhoneDemo_Click(object sender, RoutedEventArgs e) { AddBackgroundTransfer("WindowsPhone.rar", new Uri("http://files.cnblogs.com/webabcd/WindowsPhone.rar", UriKind.Absolute)); UpdateUI(); } /// <summary> /// 添加后台傳輸任務到隊列 /// </summary> /// <param name="fileName">需要在本地保存的文件名</param> /// <param name="fileUri">需要下載的文件的遠程地址</param> private void AddBackgroundTransfer(string fileName, Uri fileUri) { // 做一下相關判斷 if (BackgroundTransferService.Requests.Count() >= 5) { MessageBox.Show("每個應用程序的后台傳輸隊列最多只能有 5 個任務"); return; } if (BackgroundTransferService.Requests.Any(p => p.RequestUri == fileUri)) { MessageBox.Show("任務已在隊列中"); return; } BackgroundTransferRequest transferRequest = new BackgroundTransferRequest(fileUri); transferRequest.Method = "GET"; // 下載 transferRequest.TransferPreferences = TransferPreferences.None; // 僅在外部電源 + WiFi 環境下允許下載 transferRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged); transferRequest.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged); transferRequest.Tag = fileName; // 給 TransferRequest 賦值一個上下文數據 transferRequest.DownloadLocation = new Uri("shared/transfers/" + fileName, UriKind.Relative); try { BackgroundTransferService.Add(transferRequest); } catch (Exception ex) { MessageBox.Show("AddBackgroundTransfer: " + ex.ToString()); } } } }
2、演示如何實現后台文件傳輸(上傳)
BackgroundTransferUpload.xaml
<phone:PhoneApplicationPage x:Class="Demo.BackgroundTask.BackgroundTransferUpload" 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" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480" shell:SystemTray.IsVisible="True"> <Grid x:Name="LayoutRoot" Background="Transparent"> <StackPanel Orientation="Vertical"> <Button x:Name="btnUpload" Content="上傳 Demo.mp4 文件到服務端" Click="btnUpload_Click" /> </StackPanel> </Grid> </phone:PhoneApplicationPage>
BackgroundTransferUpload.xaml.cs
/* * 演示如何實現后台文件傳輸(上傳) * * BackgroundTransferRequest、BackgroundTransferService 及相關注意事項詳見:BackgroundTask/BackgroundTransferDownload.xaml.cs */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Phone.BackgroundTransfer; using System.IO.IsolatedStorage; using System.Windows.Resources; using System.IO; namespace Demo.BackgroundTask { public partial class BackgroundTransferUpload : PhoneApplicationPage { public BackgroundTransferUpload() { InitializeComponent(); } protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { // 為每個 BackgroundTransferRequest 注冊相關事件 foreach (var transfer in BackgroundTransferService.Requests) { transfer.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged); transfer.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged); } } private void ProcessTransfer(BackgroundTransferRequest transfer) { switch (transfer.TransferStatus) { case TransferStatus.Completed: // 200 代表上傳成功 if (transfer.StatusCode == 200) { MessageBox.Show("上傳成功"); } else { // 發生了異常,做一些相關處理 if (transfer.TransferError != null) { MessageBox.Show(transfer.TransferError.ToString()); } } RemoveTransferRequest(transfer.RequestId); break; // 注:以下 4 個狀態最好做一下判斷和處理,然后呈現給用戶 case TransferStatus.WaitingForExternalPower: break; case TransferStatus.WaitingForExternalPowerDueToBatterySaverMode: break; case TransferStatus.WaitingForNonVoiceBlockingNetwork: break; case TransferStatus.WaitingForWiFi: break; } } void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e) { ProcessTransfer(e.Request); } void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e) { // 上傳進度發生改變 } private void btnCancel_Click(object sender, EventArgs e) { string transferId = ((Button)sender).Tag as string; RemoveTransferRequest(transferId); } // 從 BackgroundTransferService 中刪除指定的傳輸請求 private void RemoveTransferRequest(string transferId) { BackgroundTransferRequest transfer = BackgroundTransferService.Find(transferId); try { if (transfer != null) BackgroundTransferService.Remove(transfer); } catch (Exception ex) { MessageBox.Show("RemoveTransferRequest: " + ex.ToString()); } } private void btnUpload_Click(object sender, RoutedEventArgs e) { // 將 Demo.mp4 文件從程序包復制到獨立存儲的 shared/transfers 目錄,以備上傳 StreamResourceInfo sr = Application.GetResourceStream(new Uri("Assets/Demo.mp4", UriKind.Relative)); using (BinaryReader br = new BinaryReader(sr.Stream)) { byte[] data = br.ReadBytes((int)sr.Stream.Length); IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication(); using (var isfs = new IsolatedStorageFileStream(@"shared/transfers/Demo.mp4", FileMode.Create, isf)) { using (var bw = new BinaryWriter(isfs)) { bw.Write(data); } } } AddBackgroundTransfer("Demo.mp4", new Uri(@"shared/transfers/Demo.mp4", UriKind.Relative)); } /// <summary> /// 添加后台傳輸任務到隊列 /// </summary> /// <param name="fileName">需要在服務端保存的文件名</param> /// <param name="fileUri">需要上傳的文件的本地地址</param> private void AddBackgroundTransfer(string fileName, Uri fileUri) { BackgroundTransferRequest transferRequest = new BackgroundTransferRequest(new Uri("http://localhost:15482/UploadFile.aspx?fileName=" + fileName, UriKind.Absolute)); transferRequest.Method = "POST"; // 上傳 transferRequest.TransferPreferences = TransferPreferences.None; // 僅在外部電源 + WiFi 環境下允許上傳 transferRequest.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferStatusChanged); transferRequest.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged); transferRequest.UploadLocation = fileUri; try { BackgroundTransferService.Add(transferRequest); } catch (Exception ex) { MessageBox.Show("AddBackgroundTransfer: " + ex.ToString()); } } } }
UploadFile.aspx.cs(用於保存上傳文件的服務端代碼)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.IO; namespace Web { public partial class UploadFile : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Request.TotalBytes > 0) { // 保存上傳過來的文件 byte[] data = Request.BinaryRead(Request.TotalBytes); File.WriteAllBytes(@"C:\" + Request.QueryString["fileName"], data); } } } }
OK
[源碼下載]