【问题标题】:Asynchronous Google File Upload with Progress Bar WPF使用进度条 WPF 异步上传 Google 文件
【发布时间】:2018-03-25 02:40:18
【问题描述】:

我正在使用服务帐户上传到 Google Cloud Storage。我需要能够在 WPF UI 中显示上传进度。现在,每当我尝试更新 ProgressBar.Value 时,它​​都不起作用,但是当我在 Console 中写入 bytesSent 时,我可以看到进度。

    public async Task<bool> UploadToGoogleCloudStorage(string bucketName, string token, string filePath, string contentType)
    {
        var newObject = new Google.Apis.Storage.v1.Data.Object()
        {
            Bucket = bucketName,
            Name = System.IO.Path.GetFileNameWithoutExtension(filePath)
        };
        var service = new Google.Apis.Storage.v1.StorageService();

        try
        {
            using (var fileStream = new FileStream(filePath, FileMode.Open))
            {

                var uploadRequest = new ObjectsResource.InsertMediaUpload(service, newObject, bucketName, fileStream, contentType);
                uploadRequest.OauthToken = token;
                ProgressBar.Maximum = fileStream.Length;
                uploadRequest.ProgressChanged += UploadProgress;
                uploadRequest.ChunkSize = (256 * 1024);
                await uploadRequest.UploadAsync().ConfigureAwait(false);
                service.Dispose();

            }

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw ex;
        }
        return true;
    }

    private void UploadProgress(IUploadProgress progress)
    {
        switch (progress.Status)
        {
            case UploadStatus.Starting:
                ProgressBar.Minimum = 0;
                ProgressBar.Value = 0;

                break;
            case UploadStatus.Completed:
                System.Windows.MessageBox.Show("Upload completed!");
                break;
            case UploadStatus.Uploading:
                //Console.WriteLine(progress.BytesSent); -> This is working if I don't call the method below.
                UpdateProgressBar(progress.BytesSent);
                break;
            case UploadStatus.Failed:
                Console.WriteLine("Upload failed "
                            + Environment.NewLine
                            + progress.Exception.Message
                            + Environment.NewLine
                            + progress.Exception.StackTrace
                            + Environment.NewLine
                            + progress.Exception.Source
                            + Environment.NewLine
                            + progress.Exception.InnerException
                            + Environment.NewLine
                            + "HR-Result" + progress.Exception.HResult);
                break;
        }
    }

    private void UpdateProgressBar(long value)
    {
        Dispatcher.Invoke(() => { this.ProgressBar.Value = value; });
    }

【问题讨论】:

  • UploadStatus.Uploading: 案例触发并且您的用户界面没有更新,还是案例甚至没有触发?如果您不确定,请在其中设置一个断点以找出答案。您需要确定这是 UI 问题还是进度报告问题。

标签: c# .net wpf progress-bar google-cloud-storage


【解决方案1】:

我尽可能多地重新创建了您的项目,并且随着图像的上传,进度条会正确更新。鉴于您的回调在写入控制台时仍然有效,可能只是您在 UI 本身中使用 ProgressBar 的方式存在问题。

继续阅读以了解我为使其正常工作所做的工作的详细信息。

这是针对Google.Cloud.Storage.V1(不是Google.Apis.Storage.v1),但现在执行上传似乎更简单一些。我从Client libraries "Getting Started" instructions 开始创建服务帐户和存储桶,然后尝试了解如何上传图片。

我遵循的过程:

  1. Sign up Google Cloud 免费试用
  2. 在 Google Cloud 中创建一个新项目(记住项目名称\ID 以备后用)
  3. Create a Project Owner service account - 这将导致下载一个包含服务帐户凭据的 json 文件。记住您放置该文件的位置。
  4. 入门文档让您将 JSON 凭据文件的路径添加到名为 GOOGLE_APPLICATION_CREDENTIALS 的环境变量中 - 我无法通过提供的说明进行操作。事实证明这不是必需的,因为您可以将 JSON 文件读入字符串并将其传递给客户端构造函数。
  5. 我创建了一个空的 WPF 项目作为起点,并创建了一个 ViewModel 来容纳应用程序逻辑。
  6. 安装Google.Cloud.Storage.V1 nuget 包,它应该会引入它需要的所有依赖项。

进入代码。

MainWindow.xaml

<StackPanel>
    <Button
        Margin="50"
        Height="50"
        Content="BEGIN UPLOAD"
        Click="OnButtonClick" />
    <ContentControl
        Content="{Binding Path=ProgressBar}" />
</StackPanel>

MainWindow.xaml.cs

public partial class MainWindow
{
    readonly ViewModel _viewModel;

    public MainWindow()
    {
        _viewModel = new ViewModel(Dispatcher);
        DataContext = _viewModel;
        InitializeComponent();
    }

    void OnButtonClick(object sender, RoutedEventArgs args)
    {
        _viewModel.UploadAsync().ConfigureAwait(false);
    }
}

ViewModel.cs

public class ViewModel
{
    readonly Dispatcher _dispatcher;

    public ViewModel(Dispatcher dispatcher)
    {
        _dispatcher = dispatcher;
        ProgressBar = new ProgressBar {Height=30};
    }

    public async Task UploadAsync()
    {
        // Google Cloud Platform project ID.
        const string projectId = "project-id-goes-here";

        // The name for the new bucket.
        const string bucketName = projectId + "-test-bucket";

        // Path to the file to upload
        const string filePath = @"C:\path\to\image.jpg";

        var newObject = new Google.Apis.Storage.v1.Data.Object
        {
            Bucket = bucketName,
            Name = System.IO.Path.GetFileNameWithoutExtension(filePath),
            ContentType = "image/jpeg"
        };

        // read the JSON credential file saved when you created the service account
        var credential = Google.Apis.Auth.OAuth2.GoogleCredential.FromJson(System.IO.File.ReadAllText(
            @"c:\path\to\service-account-credentials.json"));

        // Instantiates a client.
        using (var storageClient = Google.Cloud.Storage.V1.StorageClient.Create(credential))
        {
            try
            {
                // Creates the new bucket. Only required the first time.
                // You can also create buckets through the GCP cloud console web interface
                storageClient.CreateBucket(projectId, bucketName);
                System.Windows.MessageBox.Show($"Bucket {bucketName} created.");

                // Open the image file filestream
                using (var fileStream = new System.IO.FileStream(filePath, System.IO.FileMode.Open))
                {
                    ProgressBar.Maximum = fileStream.Length;

                    // set minimum chunksize just to see progress updating
                    var uploadObjectOptions = new Google.Cloud.Storage.V1.UploadObjectOptions
                    {
                        ChunkSize = Google.Cloud.Storage.V1.UploadObjectOptions.MinimumChunkSize
                    };

                    // Hook up the progress callback
                    var progressReporter = new Progress<Google.Apis.Upload.IUploadProgress>(OnUploadProgress);

                    await storageClient.UploadObjectAsync(
                            newObject, 
                            fileStream,
                            uploadObjectOptions,
                            progress: progressReporter)
                        .ConfigureAwait(false);
                }

            }
            catch (Google.GoogleApiException e)
                when (e.Error.Code == 409)
            {
                // When creating the bucket - The bucket already exists.  That's fine.
                System.Windows.MessageBox.Show(e.Error.Message);
            }
            catch (Exception e)
            {
                // other exception
                System.Windows.MessageBox.Show(e.Message);
            }
        }
    }

    // Called when progress updates
    void OnUploadProgress(Google.Apis.Upload.IUploadProgress progress)
    {
        switch (progress.Status)
        {
            case Google.Apis.Upload.UploadStatus.Starting:
                ProgressBar.Minimum = 0;
                ProgressBar.Value = 0;

                break;
            case Google.Apis.Upload.UploadStatus.Completed:
                ProgressBar.Value = ProgressBar.Maximum;
                System.Windows.MessageBox.Show("Upload completed");

                break;
            case Google.Apis.Upload.UploadStatus.Uploading:
                UpdateProgressBar(progress.BytesSent);

                break;
            case Google.Apis.Upload.UploadStatus.Failed:
                System.Windows.MessageBox.Show("Upload failed"
                                               + Environment.NewLine
                                               + progress.Exception);
                break;
        }
    }

    void UpdateProgressBar(long value)
    {
        _dispatcher.Invoke(() => { ProgressBar.Value = value; });
    }

    // probably better to expose progress value directly and bind to 
    // a ProgressBar in the XAML
    public ProgressBar ProgressBar { get; }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 2016-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多