1. 程式人生 > >背水一戰 Windows 10 (120) - 後臺任務: 後臺上傳任務

背水一戰 Windows 10 (120) - 後臺任務: 後臺上傳任務

prope message avi title visual AD tex jpg parent

[源碼下載]


背水一戰 Windows 10 (120) - 後臺任務: 後臺上傳任務



作者:webabcd


介紹
背水一戰 Windows 10 之 後臺任務

  • 後臺上傳任務



示例
演示 uwp 的後臺上傳任務
BackgroundTask/TransferModel.cs

/*
 * 擴展了 DownloadOperation 和 UploadOperation,用於 MVVM 綁定數據
 */

using System;
using System.ComponentModel;
using Windows.Networking.BackgroundTransfer;

namespace Windows10.BackgroundTask { public class TransferModel : INotifyPropertyChanged { public DownloadOperation DownloadOperation { get; set; } public UploadOperation UploadOperation { get; set; } public string Source { get; set; } public string Destination { get
; set; } private string _progress; public string Progress { get { return _progress; } set { _progress = value; RaisePropertyChanged("Progress"); } } public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } }

BackgroundTask/TransferUpload.xaml

<Page
    x:Class="Windows10.BackgroundTask.TransferUpload"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.BackgroundTask"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <ScrollViewer Name="scrollViewer" Height="100" Margin="5">
                <TextBlock Name="lblMsg" TextWrapping="Wrap" />
            </ScrollViewer>

            <Button Name="btnAddUpload" Content="新增一個上傳任務(一次請求上傳一個文件)" Margin="5" Click="btnAddUpload_Click" />
            <Button Name="btnAddMultiUpload" Content="新增一個上傳任務(一次請求上傳多個文件)" Margin="5" Click="btnAddMultiUpload_Click" />
            <Button Name="btnCancel" Content="取消所有上傳任務" Margin="5" Click="btnCancel_Click" />

            <ListView Name="listView" Height="286" Padding="5">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Margin="0 5" Background="Blue">
                            <TextBlock Text="{Binding Source}" Margin="5" />
                            <TextBlock Text="{Binding Destination}" Margin="5" />
                            <TextBlock Text="{Binding Progress}" Margin="5" />
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

        </StackPanel>
    </Grid>
</Page>

BackgroundTask/TransferUpload.xaml.cs

/*
 * 演示 uwp 的後臺上傳任務
 * 
 * BackgroundUploader - 後臺上傳任務管理器
 *     CreateUpload(Uri uri, IStorageFile sourceFile) - 創建一個上傳任務,返回 UploadOperation 對象
 *     CreateUploadFromStreamAsync(Uri uri, IInputStream sourceStream) - 以流的方式創建一個上傳任務
 *     CreateUploadAsync(Uri uri, IEnumerable<BackgroundTransferContentPart> parts) - 創建一個包含多個上傳文件的上傳任務
 *     SetRequestHeader(string headerName, string headerValue) - 設置 http 請求頭
 *     Method - 用於上傳的 http method(默認 post)
 *     static GetCurrentUploadsAsync() - 獲取當前 app 的未與組關聯的所有上傳任務
 *     CostPolicy - 上傳的成本策略,BackgroundTransferCostPolicy 枚舉
 *         Default - 不允許在高成本(比如 4G)網絡上傳輸
 *         UnrestrictedOnly - 允許在高成本(比如 4G)網絡上傳輸
 *         Always - 無論如何均可傳輸,即使在漫遊時
 *     ServerCredential - 與服務端通信時的憑據
 *     ProxyCredential - 使用代理時的身份憑據
 *     SuccessToastNotification, SuccessTileNotification, FailureToastNotification, FailureTileNotification - 上傳任務成功或失敗後的 toast 或 tile 通知
 *     static GetCurrentUploadsForTransferGroupAsync(BackgroundTransferGroup group) - 獲取指定組的所有上傳任務
 *     TransferGroup - 設置或獲取分組對象(BackgroundTransferGroup 類型)
 *     BackgroundUploader(BackgroundTransferCompletionGroup completionGroup) - 通過指定的 BackgroundTransferCompletionGroup 對象實例化 BackgroundUploader 對象
 *     CompletionGroup - 獲取關聯的 BackgroundTransferCompletionGroup 對象
 *     
 * UploadOperation - 上傳任務對象
 *     Guid - 獲取此上傳任務的標識
 *     CostPolicy - 上傳的成本策略,BackgroundTransferCostPolicy 枚舉
 *     RequestedUri - 上傳任務所請求的服務端地址
 *     SourceFile - 需要上傳的文件,如果是一次上傳多個文件則此屬性為 null
 *     Method - 獲取用於上傳的 http method(get, post 之類的)
 *     GetResponseInformation() - 上傳完成後獲取到的服務端響應信息,返回 ResponseInformation 對象
 *         ActualUri - 上傳服務的真實 URI
 *         Headers - 服務端響應的 HTTP 頭
 *         StatusCode - 服務端響應的狀態碼
 *     StartAsync() - 新增一個上傳任務,返回 IAsyncOperationWithProgress<UploadOperation, UploadOperation> 對象
 *     AttachAsync() - 監視已存在的上傳任務,返回 IAsyncOperationWithProgress<UploadOperation, UploadOperation> 對象
 *     Progress - 獲取上傳進度,返回 BackgroundUploadProgress 對象
 *     Priority - 上傳的優先級,BackgroundTransferPriority 枚舉
 *         Default 或 High
 *     TransferGroup - 獲取此上傳任務的分組對象(BackgroundTransferGroup 類型)
 *     
 * BackgroundUploadProgress - 後臺上傳任務的上傳進度對象
 *     BytesSent - 已上傳的字節數
 *     TotalBytesToSend - 總共需要上傳的字節數
 *     BytesReceived - 已下載的字節數
 *     TotalBytesToReceive - 總共需要下載的字節數,未知則為 0 
 *     Status - 上傳狀態,BackgroundTransferStatus 枚舉
 *         Idle, Running, PausedByApplication, PausedCostedNetwork, PausedNoNetwork, Completed, Canceled, Error
 *     HasResponseChanged - 服務端響應了則為 true
 *     HasRestarted - 當上傳連接斷掉後,系統會重新上傳,此種情況則為 true
 *     
 * BackgroundTransferGroup - 後臺上傳任務的分組對象
 *     static BackgroundTransferGroup CreateGroup(string name) - 創建指定分組標識的 BackgroundTransferGroup 對象
 *     Name - 分組標識(只讀)
 *     TransferBehavior - 組內上傳任務的執行方式,BackgroundTransferBehavior 枚舉
 *         Parallel - 並行
 *         Serialized - 串行
 * 
 * BackgroundTransferCompletionGroup - 分組對象(用於實現“組任務全部完成後觸發後臺任務”)
 *     Enable() - 啟用“組任務全部完成後觸發後臺任務”的功能
 *     IsEnabled - 是否啟用了“組任務全部完成後觸發後臺任務”的功能(只讀)
 *     Trigger - “組任務全部完成後觸發後臺任務”的觸發器
 *     
 * BackgroundTransferContentPart - 當一次上傳多個文件時,將每個需要上傳的文件構造成一個 BackgroundTransferContentPart 對象
 *     BackgroundTransferContentPart(string name, string fileName) - 通過一個標識和文件名稱實例化 BackgroundTransferContentPart 對象
 *     SetFile(IStorageFile value) - 指定需要上傳的文件
 *     
 * 
 * 註:關於上傳任務的“任務分組,並行或串行執行,組完成後通知”和“任務分組,組完成後觸發後臺任務”的實現方式與下載任務是一樣的,請參見下載任務的相關演示示例(TransferGroup.xaml.cs 和 TransferBackground.xaml.cs)
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Windows.Data.Xml.Dom;
using Windows.Networking.BackgroundTransfer;
using Windows.Storage;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Web;

namespace Windows10.BackgroundTask
{
    public sealed partial class TransferUpload : Page
    {
        // 上傳任務的集合
        private ObservableCollection<TransferModel> _transfers = new ObservableCollection<TransferModel>();

        // 所有上傳任務的關聯的 CancellationTokenSource 對象
        private CancellationTokenSource _cancelToken = new CancellationTokenSource();

        public TransferUpload()
        {
            this.InitializeComponent();

            Init();
        }

        private async void Init()
        {
            listView.ItemsSource = _transfers;

            // 加載全部上傳任務
            await LoadUploadAsync();
        }

        // 加載全部上傳任務
        private async Task LoadUploadAsync()
        {
            IReadOnlyList<UploadOperation> uploads = null;
            try
            {
                // 獲取所有後臺上傳任務
                uploads = await BackgroundUploader.GetCurrentUploadsAsync();
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
                return;
            }

            if (uploads.Count > 0)
            {
                List<Task> tasks = new List<Task>();
                foreach (UploadOperation upload in uploads)
                {
                    // 監視指定的後臺上傳任務
                    tasks.Add(HandleUploadAsync(upload, false));
                }

                await Task.WhenAll(tasks);
            }
        }

        // 新增一個上傳任務(一次請求上傳一個文件)
        private async void btnAddUpload_Click(object sender, RoutedEventArgs e)
        {
            // 上傳服務的地址
            Uri serverUri = new Uri("http://localhost:44914/api/Upload", UriKind.Absolute);

            StorageFile sourceFile;
            try
            {
                // 需要上傳的文件
                sourceFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/hololens.jpg", UriKind.Absolute));
            }
            catch (Exception ex)
            {
                WriteLine(ex.ToString());
                return;
            }

            // 實例化 BackgroundUploader,並設置 http header
            BackgroundUploader backgroundUploader = new BackgroundUploader();
            backgroundUploader.SetRequestHeader("Filename", "hololens.jpg");

            // 任務成功後彈出指定的 toast 通知(類似的還有 SuccessTileNotification, FailureToastNotification, FailureTileNotification)
            backgroundUploader.SuccessToastNotification = GetToastNotification();

            // 創建一個後臺上傳任務,此任務包含一個上傳文件
            UploadOperation upload = backgroundUploader.CreateUpload(serverUri, sourceFile);

            // 以流的方式創建一個後臺上傳任務
            // await backgroundUploader.CreateUploadFromStreamAsync(Uri uri, IInputStream sourceStream);

            // 處理並監視指定的後臺上傳任務
            await HandleUploadAsync(upload, true);
        }

        // 新增一個上傳任務(一次請求上傳多個文件)
        private async void btnAddMultiUpload_Click(object sender, RoutedEventArgs e)
        {
            // 上傳服務的地址
            Uri serverUri = new Uri("http://localhost:44914/api/Upload", UriKind.Absolute);

            // 需要上傳的文件源集合
            List<StorageFile> sourceFiles = new List<StorageFile>();
            for (int i = 0; i < 3; i++)
            {
                StorageFile sourceFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/hololens.jpg", UriKind.Absolute));
                sourceFiles.Add(sourceFile);
            }

            // 構造需要上傳 BackgroundTransferContentPart 集合
            List<BackgroundTransferContentPart> contentParts = new List<BackgroundTransferContentPart>();
            for (int i = 0; i < sourceFiles.Count; i++)
            {
                BackgroundTransferContentPart contentPart = new BackgroundTransferContentPart("File" + i, sourceFiles[i].Name);
                contentPart.SetFile(sourceFiles[i]);
                contentParts.Add(contentPart);
            }
            
            BackgroundUploader backgroundUploader = new BackgroundUploader();

            // 任務成功後彈出指定的 toast 通知(類似的還有 SuccessTileNotification, FailureToastNotification, FailureTileNotification)
            backgroundUploader.SuccessToastNotification = GetToastNotification();

            // 創建一個後臺上傳任務,此任務包含多個上傳文件
            UploadOperation upload = await backgroundUploader.CreateUploadAsync(serverUri, contentParts);

            // 處理並監視指定的後臺上傳任務
            await HandleUploadAsync(upload, true);
        }

        /// <summary>
        /// 處理並監視指定的後臺上傳任務
        /// </summary>
        /// <param name="upload">後臺上傳任務</param>
        /// <param name="isNew">是否是新增的任務</param>
        private async Task HandleUploadAsync(UploadOperation upload, bool isNew)
        {
            try
            {
                // 將 UploadOperation 附加到 TransferModel,以便上傳進度可通知
                TransferModel transfer = new TransferModel();
                transfer.UploadOperation = upload;
                transfer.Source = "多個文件";
                transfer.Destination = upload.RequestedUri.ToString();
                transfer.Progress = upload.Progress.Status.ToString() + "0 / 0";

                _transfers.Add(transfer);

                WriteLine("Task Count: " + _transfers.Count.ToString());

                // 當上傳進度發生變化時的回調函數
                Progress<UploadOperation> progressCallback = new Progress<UploadOperation>(UploadProgress);

                if (isNew)
                    await upload.StartAsync().AsTask(_cancelToken.Token, progressCallback); // 啟動一個後臺上傳任務
                else
                    await upload.AttachAsync().AsTask(_cancelToken.Token, progressCallback); // 監視已存在的後臺上傳任務

                // 上傳完成後獲取服務端的響應信息
                ResponseInformation response = upload.GetResponseInformation();
                WriteLine("Completed: " + response.ActualUri + ", HttpStatusCode: " + response.StatusCode.ToString());
            }
            catch (TaskCanceledException) // 調用 CancellationTokenSource.Cancel() 後會拋出此異常
            {
                WriteLine("Canceled: " + upload.Guid);
            }
            catch (Exception ex)
            {
                // 將異常轉換為 WebErrorStatus 枚舉,如果獲取到的是 WebErrorStatus.Unknown 則說明此次異常不是涉及 web 的異常
                WebErrorStatus error = BackgroundTransferError.GetStatus(ex.HResult);

                WriteLine(ex.ToString());
            }
            finally
            {
                _transfers.Remove(_transfers.First(p => p.UploadOperation == upload));
            }
        }

        // 進度發生變化時,更新 TransferModel 的 Progress
        private void UploadProgress(UploadOperation upload)
        {
            TransferModel transfer = _transfers.First(p => p.UploadOperation == upload);
            transfer.Progress = upload.Progress.Status.ToString() + ": " + upload.Progress.BytesSent.ToString("#,0") + " / " + upload.Progress.TotalBytesToSend.ToString("#,0");
        }

        // 取消全部後臺上傳任務
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            _cancelToken.Cancel();
            _cancelToken.Dispose();

            _cancelToken = new CancellationTokenSource();
        }

        // 向 lblMsg 中追加一行文本
        private void WriteLine(string message)
        {
            lblMsg.Text += message;
            lblMsg.Text += Environment.NewLine;

            scrollViewer.ChangeView(0, scrollViewer.ScrollableHeight, 1f);
        }

        // 構造指定的 toast 通知
        private ToastNotification GetToastNotification()
        {
            string toastXml = $@"
                <toast activationType=‘foreground‘>
                    <visual>
                        <binding template=‘ToastGeneric‘>
                            <text>toast - title</text>
                            <text>上傳任務成功完成</text>
                        </binding>
                    </visual>
                </toast>";
            
            XmlDocument toastDoc = new XmlDocument();
            toastDoc.LoadXml(toastXml);

            return new ToastNotification(toastDoc);
        }
    }
}



OK
[源碼下載]

背水一戰 Windows 10 (120) - 後臺任務: 後臺上傳任務