后台文件傳輸
后台文件傳輸僅支持HTTP和HTTPS的傳輸,不支持 FTP。通過使用 BackgroundTransferService 實現后台上傳和下載文件。GET HTTP方法支持下載文件,POST方法支持下載或上傳文件。傳輸方法是在 BackgroundTransferRequest 對象的Method 屬性里設置的。
后台文件傳輸對於文件系統的限制是所有后台傳輸中下載的目標路徑和上傳的源路徑都必須是本地文件路徑。下載的目標路徑指定保存下載的文件的位置,上傳的源路徑指定上傳文件的位置。后台傳輸的所有本地路徑都必須位於您應用程序的獨立存儲中,在名為"/shared/transfers"的根目錄中。該目錄是操作系統在安裝應用程序時創建的,但如果應用程序刪除或重命名該目錄,則必須在啟動任何文件傳輸之前重新創建該目錄。您可以在"/shared/transfers"根目錄下創建您選擇的任何其他目錄結構,並且可以在傳輸完成之后復制或刪除文件。嘗試使用"/shared/transfers"目錄之外的路徑啟動傳輸將引發異常,所以接受該默認的下載目錄即可,在下載完成后再將文件復制至其他目錄。
后台傳輸的策略
- 大小
上傳最大文件大小 |
5MB |
通過手機網絡連接的最大下載大小 |
20MB——如果超過此限制,則傳輸的TransferPreferences屬性將自動更改為AllowBattery,它對需要Wi-Fi的傳輸有影響。 |
通過Wi-Fi而沒有外部電源的最大下載大小 |
100 MB——大於100 MB的文件必須將傳輸的TransferPreferences屬性設置為None,否則傳輸將失敗。如果不知道傳輸文件的大小,則其有可能會超出該限制,您應將值設置為None。 |
- 限制
每個應用程序的隊列中未完成的最大請求數(這包括活動和掛起的請求)。 |
5——完成后不會自動從隊列中刪除傳輸。應用程序應該使用Remove(BackgroundTransferRequest)從隊列中刪除完成的傳輸,以便為新的傳輸留出隊列空間。 |
設備上所有應用程序的最大並發傳輸數 |
2 |
設備上所有應用程序的最大排隊傳輸數 |
500 |
每個請求的最大HTTP標頭數 |
15 |
HTTP標頭的最大大小 |
每個16KB |
- 策略
-
在非並發語音和數據網絡上不運行后台傳輸服務,其中包括:
- 2G、EDGE、標准 GPRS
后台傳輸服務在3G以及更高版本的網絡上運行。
- 服務器端所需的文件內容長度標頭大於5MB。服務器應該始終在響應中返回內容長度。不這樣做可能會導致嚴重降低傳輸的性能。
- 服務器端所需的文件范圍標頭大於 5MB。服務器應該始終支持范圍請求標頭。不這樣做可能會導致嚴重降低傳輸的性能。
- 慢速傳輸
如果設備的網絡連接速度低於以下速率,則會暫停傳輸並重試。這些限制(單位為Kb/s)比較低,因此通常不會達到。
網絡媒介 |
最低數據速率 |
3G |
50 Kbps |
Wi-Fi/USB |
100 Kbps |
后台傳輸程序的認證要求
后台音頻的程序屬於特定應用程序類型,除了一般應用程序的要求需滿足外,還需符合如下的要求。
- 該應用程序不得啟動后台傳輸,除非用戶激活該應用程序提供的可發UI元素。
驗證方法:
- 啟動應用程序;
- 驗證該應用程序中是否存在可發現的 UI 元素,並允許后台傳輸;
- 激活 UI 元素以開始后台傳輸;
- 驗證是否開始后台傳輸。
-
當應用程序位於前台中時,該應用程序必須允許用戶通過可發現的UI元素查看所有活動和掛起的后台傳輸狀態。
驗證方法:
- 啟動應用程序;
- 啟動后台傳輸;
- 驗證是否存在可發現的UI元素,用於顯示正在進行的傳輸;
- 驗證UI元素是否正確顯示后台傳輸;
- 激活用於停止后台傳輸的UI元素;
- 驗證確認進度UI元素不顯示已停止的后台傳輸。
-
該應用程序必須為用戶提供可發現的UI元素,並允許用戶取消活動或掛起的后台傳輸。
驗證方法:
- 啟動應用程序;
- 啟動后台傳輸;
- 驗證是否存在可發現的UI元素,並允許用戶取消后台傳輸;
- 激活UI元素以停止后台傳輸;
- 驗證確認后台傳輸停止並且不再顯示在相關的UI元素中。
最佳實踐
-
應用程序啟動
- 使用BackgroundTransferService對象的Requests屬性輪詢應用程序的所有后台傳輸。
- 對於每個請求,為TransferStatusChanged事件注冊一個處理程序以便您的應用程序可以響應該應用程序正在運行時發生的狀態更改。
- 對於每個請求,為TransferProgressChanged事件注冊一個處理程序。這對於通知用戶有關活動傳輸進度的信息非常有用。
- 對於每個請求,檢查TransferStatus屬性,以確定該應用程序處於非活動狀態時任何傳輸的狀態是否為已完成或已更改。
- 使用前面提到的回調函數更新應用程序UI,而不是使用計時器或其他一些輪詢服務更改的機制。
-
添加新的傳輸請求
- 使用BackgroundTransferService對象的Add(BackgroundTransferRequest)方法。
- 首先,查看應用程序是否已達到每個應用程序5個並發傳輸的限制。如果已達到,則可以通知用戶,提示用戶等待現有傳輸完成或取消現有傳輸。也可以將有關新傳輸的信息存儲在獨立存儲中,然后在將來的某些時間文件傳輸隊列低於此限制時加載該信息並啟動傳輸。
- 將對Add的調用放置在一個try塊中並捕獲任何異常。嘗試創建新的傳輸失敗時,您應該向用戶提供一個可使用的消息。
- 使用"/shared/transfers"作為所有傳輸操作的本地根目錄。對於文件下載,請將DownloadLocation屬性設置為該目錄中的某個文件名。這就是傳輸完成時文件所在的位置。對於文件上傳,請將UploadLocation屬性設置為該目錄中的某個文件名以指定要上傳的文件。
- 使用RequestUri屬性指定文件傳輸的遠程服務器地址。后台傳輸服務使用Uri的 OriginalString 屬性。因此,應該使用Uri.EscapeUriString方法轉義Uri中的任何特殊字符(如果Uri尚未轉義)。
- 如果要傳輸的文件大於100 MB,請將傳輸的TransferPreferences的屬性設置為None,否則傳輸將失敗。如果不知道傳輸文件的大小,則其有可能會超出該限制,也應將該屬性設置為None。
-
傳輸完成
- TransferStatu屬性將擁有值Completed。
- 通過檢查包含從目標服務器返回的HTTP狀態代碼的StatusCode屬性確定傳輸是否成功。如果傳輸成功,則該值將為200或206。建議這兩個值都檢查。其他任何狀態代碼都指示服務器錯誤。處理服務器錯誤的方式取決於您的應用程序。
- 檢查TransferError屬性以幫助確定失敗的傳輸未成功的原因。
- 如果文件下載成功,您可能希望將該文件從"\shared\transfers"目錄移動到獨立存儲中的某個新位置。
- 通過調用BackgroundTransferService的Remove(BackgroundTransferRequest)方法從隊列中刪除傳輸請求。應用程序限制最多同時存在5個並發傳輸請求,包括掛起的、活動的以及已完成的請求。系統不會自動刪除已完成的傳輸。如果您刪除舊傳輸失敗且超過了應用程序限制,則當您嘗試添加新的傳輸時會引發異常。
-
UI設計的最佳實踐
- 后台文件傳輸應該由用戶啟動,如通過點按某個按鈕。如果用戶未啟動傳輸,則應用程序應該通知用戶該應用程序正在代表用戶啟動傳輸。
- 您必須提供允許用戶查看所有后台傳輸的狀態和傳輸進度的 UI。
- 必須提供用戶取消任何以及所有當前傳輸的機制。
- 使用將文件傳輸限制為僅通過 Wi-Fi 以及在連有外部電源時進行的默認傳輸首選項。提供可讓用戶選擇在沒有外部電源的情況下允許通過手機網絡連接進行傳輸的UI。可以使用TransferPreferences屬性設置用戶的選擇。
- 如果后台傳輸的狀態為WaitingForExternalPower、WaitingForExternalPowerDueToBatterySaverMode、WaitingForWiFi或WaitingForNonVoiceBlockingNetwork,則向用戶提供一個消息,通知他們傳輸正在等待的原因,最好也通知他們恢復傳輸(例如連接到Wi-Fi網絡)所需的步驟。
-
其他建議
- 在關聯的傳輸操作完成之前,不要嘗試訪問或修改"/shared/transfers"目錄中的目標文件。
- BackgroundTransferRequest對象的 Tag 屬性可用於將關聯的自定義數據與某個傳輸關聯。應用程序可以在創建傳輸請求時設置值。當使用Requests屬性或Find(String)方法檢索傳輸請求時,Tag屬性將包含以前設置的數據。該屬性的最大長度為 4000個字符,但建議您保持較小的數據大小以便提高性能。
- TransferStatusChanged和TransferProgressChanged事件的事件處理程序應該快速返回以便用戶界面不會變得行動遲緩。從獨立存儲中讀取以及向獨立存儲中寫入的速度可能很慢,如果可能的話,應該在單獨的工作線程上執行。
- TotalBytesToReceive屬性指示文件下載的總大小。如果該值為-1,則表示文件大小未知。
- 服務器應該在HTTP響應標頭中返回內容長度,以便讓用戶知道傳輸的大小和進度。
- 服務器必須支持來自客戶端的Range請求以改進性能。后台傳輸有可能會在進行時暫停和恢復。不支持 Range 請求將導致性能降低,因為傳輸一旦暫停,必須重新啟動。
動手實踐——后台下載3D模型
本節使用的代碼是通過使用組合Silverlight和XNA的項目模板。在Windows Phone OS 7.1中啟動時,可將Silverlight和XNA內容合並到同一個應用程序或游戲。這樣您可以使用Silverlight將多格式文本支持功能添加到您的XNA游戲,而且您還可以將包含XNA內容的頁面添加到Silverlight應用程序。本節重點講解如何通過后台下載功能實現遠程下載XNA模型。
首先在Silverlight的MainPage.xaml中顯示下載列表,使用Visual Studio 2010打開解決方案ModelViewer\ModelViewer.sln,其中MainPage.xaml在設計視圖中顯示內容如圖14-1 MainPage。列表分為兩個部分,本地模型列表和遠程模型列表。本例中使用Windows Phone Mango提供的BackgroundTransferService類創建和管理下載功能。當用戶在遠程XNA模型列表中選擇XNA模型,點擊下載按鈕后。應用程序將XNA模型下載至手機中,並將其從遠程模型列表移動到本地模型列表。
圖18-1 MainPage
后台傳輸服務的實現方法:創建后台傳輸請求BackgroundTransferRequest,添加至后台傳輸服務BackgroundTransferService隊列。
BackgroundTransferRequest類需要添加引用Microsoft.Phone.BackgroundTransfer。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
using Microsoft.Phone.BackgroundTransfer;
Download類的Start方法實現創建后台傳輸服務請求。requestUri聲明下載地址,downloadUri聲明保存的地址。當應用程序將下載請求增加到后台下載服務BackgroundTransferService的隊列中后,Windows Phone操作系統在后台任務重中執行下載。此時,即使應用程序不處於激活狀態,下載過程仍在繼續。
在本例中,應用程序監聽TransferStatusChanged事件和TransferProgressChanged事件。TransferStatusChanged事件傳遞BackgroundTransferEventArgs對象,通過判斷下載請求的TransferStatus屬性。此時因為應用程序不再需要監聽后台傳輸請求,所以就執行取消TransferStatusChanged事件的訂閱操作。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
public void Start()
{
// 創建后台文件傳輸的請求
BackgroundTransferRequest request = new BackgroundTransferRequest(requestUri, downloadUri);
request.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
requestId = request.RequestId;
// 訂閱傳輸事件TransferStatusChanged和TransferProgressChanged
request.TransferStatusChanged += request_TransferStatusChanged;
request.TransferProgressChanged += request_TransferProgressChanged;
// 在后台傳輸服務中添加請求
BackgroundTransferService.Add(request);
}
private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;
// 傳輸完成,取消訂閱
if(request.TransferStatus == TransferStatus.Completed)
{
request.TransferStatusChanged -= request_TransferStatusChanged;
request.TransferProgressChanged -= request_TransferProgressChanged;
if(isAborted)
OnDownloadAborted();
else
OnDownloadFinished(request);
}
}
注意:
后台傳輸請求可能不會立即執行,在后台傳輸服務使用一個特殊的調度程序來管理下載請求。例如,當電池電量有限的情況下,操作系統可能會暫停傳輸,以減少能源消耗。
大於100 MB的文件必須將傳輸的TransferPreferences屬性設置為None,否則傳輸將失敗。如果不知道傳輸文件的大小,則其有可能會超出該限制,建議應將其值設置為None。
當下載完成時request_TransferStatusChanged事件處理函數移動文件,通知應用程序從隊列中刪除的BackgroundTransferRequest。Windows Phone不會自動從隊列中自動刪除下載請求,必須由應用程序開發人員編寫代碼執行。OnDownloadAborted方法刪除下載的臨時文件,並且通知下載取消的事件。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
public event EventHandler DownloadFinished;
public event EventHandler DownloadAborted;
private void OnDownloadFinished(BackgroundTransferRequest request)
{
// 將完成下載的文件從"/shared/transfers"移至目標目錄
Storage.MoveFile(downloadPath, targetPath);
// 更新本地模型列表和遠程模型列表的UI
if(DownloadFinished != null)
DownloadFinished(this, EventArgs.Empty);
// 從BackgroundTransferService 移除已經完成下載任務的請求
BackgroundTransferService.Remove(request);
}
private void OnDownloadAborted()
{
// 刪除臨時下載文件,即"/shared/transfers"目錄下的文件
Storage.DeleteFile(downloadPath);
// 取消正在下載的任務
if(DownloadAborted != null)
DownloadAborted(this, EventArgs.Empty);
}
為實現在用戶界面的藍色進度條的顯示,需響應傳輸請求BackgroundTransferRequest的傳輸進度TransferProgressChanged事件。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
public event EventHandler<DownloadProgressEventArgs> DownloadProgress;
private void request_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;
// 只要傳輸還在繼續則更新進度條
if(request.TransferStatus == TransferStatus.Transferring)
{
// 更新進度
if(DownloadProgress != null)
DownloadProgress(this, new DownloadProgressEventArgs(request.BytesReceived, request.TotalBytesToReceive));
}
}
圖18-2 后台傳輸進度條
注意:
當應用程序處於休眠狀態或者停止運行,后台傳輸服務都會持續執行。在此期間,后台傳輸服務將所有的過程和狀態事件存放在隊列中,當應用程序重新激活時將接收到所有的相關更新事件。
此應用程序允許用戶中止正在進行的下載。當下載開始,遠程模型列表中的下載按鈕顯示"X",表示停止下載。用戶點擊該按鈕,ModelMetadata類將調用Download類的Abort方法停止下載。
在MainPage.xaml中遠程模型列表ListBox,綁定SelectionChanged事件處理函數和Item模板ModelDT。
Silverlight/XNA Project: ModelViewer File: MainPage.xaml
<ListBox x:Name="lstLocalModels" ItemsSource="{Binding LocalModels}"
SelectionChanged="lstLocalModels_SelectionChanged" ItemTemplate="{StaticResource ModelDT}" Margin="0" Grid.Row="1" BorderBrush="Red" />
在MainPage.xaml中定義列表項的數據模板,數據模板中的包含綁定Name和Description的TextBlock,以及下載進度條ProgressBar。
Silverlight/XNA Project: ModelViewer File: MainPage.xaml
<DataTemplate x:Key="ModelDT">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="280" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="0,0,0,2" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="7" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" Grid.Row="0" FontSize="{StaticResource PhoneFontSizeLarge}" Margin="12,-2,12,0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock Text="{Binding Description}" Margin="12,-5,12,0" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Top" />
<ProgressBar Minimum="0" Maximum="100"
Value="{Binding DownloadProgress}"
Visibility="{Binding IsInProgress, Converter={StaticResource BoolToVisibilityConverter}}" Margin="0" Grid.Row="2" MinHeight="5" VerticalAlignment="Top" Width="468"
/>
</Grid>
<Button
Grid.Column="1"
Margin="0"
Padding="0"
Click="abortButton_Click"
BorderThickness="0"
Foreground="{StaticResource PhoneAccentBrush}"
Width="60"
Height="60"
Visibility="{Binding IsInProgress, Converter={StaticResource BoolToVisibilityConverter}}"
>
<Border
Height="30"
BorderBrush="{StaticResource PhoneAccentBrush}"
BorderThickness="2"
CornerRadius="15"
Padding="10,2,10,4" >
<TextBlock
Text="X"
FontSize="{StaticResource PhoneFontSizeSmall}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
</Button>
</Grid>
</DataTemplate>
Download類的Abort方法實現取消下載的功能,Abort方法調用后台傳輸服務檢索定制requestId的后台傳輸服務請求,並從后台傳輸服務的隊列中刪除請求。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
public bool Abort()
{
BackgroundTransferRequest request = BackgroundTransferService.Find(requestId);
if(request != null)
{
isAborted = true;
BackgroundTransferService.Remove(request);
}
return request != null;
}
注意:
后台傳輸服務API只提供應用程序訪問自己的傳輸服務請求隊列,不能訪問其他的應用程序的傳輸請求隊列。
當后台傳輸服務請求BackgroundTransferRequest從隊列中刪除時,后台傳輸服務BackgroundTransferRequest觸發TransferStatusChanged,並將傳輸狀態屬性設置為TransferStatus.Completed。由於下載完成的傳輸狀態屬性也為TransferStatus.Completed,所以在Download類的Abort方法中設置isAborted為True。以此來區分下載取消和下載完成。
Silverlight/XNA Project: ModelViewer File: Downloads \Download.cs
private void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
BackgroundTransferRequest request = e.Request;
if(request.TransferStatus == TransferStatus.Completed)
{
request.TransferStatusChanged -= request_TransferStatusChanged;
request.TransferProgressChanged -= request_TransferProgressChanged;
if(isAborted)
OnDownloadAborted();
else
OnDownloadFinished(request);
}
}
Windows Phone Mango限制后台傳輸請求隊列大小為5項,向隊列中添加更多的請求會導致應用程序異常。
DownloadManager類管理下載隊列,DownloadManager的StartDownload和ProcessPendingDownloads方法演示如何使用隊列。
Silverlight/XNA Project: ModelViewer File: Downloads \DownloadManager.cs
private static readonly Collection<Download> downloads = new Collection<Download>();
private static readonly Queue<Download> pendingDownloads = new Queue<Download>();
public static void StartDownload(Download download)
{
if(BackgroundTransferService.Requests.Count() < 5)
{
download.DownloadFinished += download_DownloadFinished;
download.DownloadAborted += download_DownloadAborted;
download.Start();
}
else
{
pendingDownloads.Enqueue(download);
}
}
private static void ProcessPendingDownloads()
{
if(pendingDownloads.Count > 0)
{
Download download = pendingDownloads.Dequeue();
StartDownload(download);
}
}
當應用程序請求的下載數量達到極限時,下載請求將保存在pendingDownloads隊列而不是BackgroundTransferService隊列。本例中pendingDownloads隊列在應用程序邏輯刪除或者關閉時將被刪除。