Asynchronous Google File Upload with Progress Bar WPF

时光总嘲笑我的痴心妄想 提交于 2021-02-18 12:36:06

问题


I am uploading to Google Cloud Storage using a service account. I need to be able to display the progress of the upload in the WPF UI. Now, Whenever I try to update the ProgressBar.Value, it's not working, but when I just have the bytesSent written in Console, I can see the progress.

    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; });
    }

回答1:


I re-created as much of your project as I could, and the progress bar updates correctly as the image uploads. Given your callback still works when writing to the console it's possible there's just an issue with how you're using the ProgressBar in the UI itself.

Read on to see the details of what I did to get it working.

This is for Google.Cloud.Storage.V1 (not Google.Apis.Storage.v1), but appears to be a bit simpler to perform an upload now. I started with the Client libraries "Getting Started" instructions to create a service account and bucket, then experimented to find out how to upload an image.

The process I followed:

  1. Sign up for Google Cloud free trial
  2. Create a new project in Google Cloud (remember the project name\ID for later)
  3. Create a Project Owner service account - this will result in a json file being downloaded that contains the service account credentials. Remember where you put that file.
  4. The getting started docs get you to add the path to the JSON credentials file into an environment variable called GOOGLE_APPLICATION_CREDENTIALS - I couldn't get this to work through the provided instructions. Turns out it is not required, as you can just read the JSON file into a string and pass it to the client constructor.
  5. I created an empty WPF project as a starting point, and a single ViewModel to house the application logic.
  6. Install the Google.Cloud.Storage.V1 nuget package and it should pull in all the dependencies it needs.

Onto the code.

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; }
}


来源:https://stackoverflow.com/questions/49472013/asynchronous-google-file-upload-with-progress-bar-wpf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!