获取进度 information/make .NET 5 Blazor PostAsJsonAsync 中文件上传的进度条

Getting progress information/make a progress bar from file upload in .NET 5 Blazor PostAsJsonAsync

我正在我的 (.Net5) Blazor WebAssembly 应用程序中上传多个文件。 我有一个 'ObjectDTO',其中包含信息和文件对象列表,如下所示:

        public class ObjectDTO
        {
            public int Id { get; set; }


            public string Name { get; set; }


            public string Description { get; set; }

            public decimal ActualCost { get; set; }   // Not sent to app.

            public decimal NormalRetailPrice { get; set; }

            public string StatusMsg { get; set; }

            public ICollection<ExternalFileDTO> ImageFiles { get; set; } // Videos/Images 

        }

'Icollection' 是包含文件的对象,如下所示:

        public class ExternalFileDTO
        {
            public int Id { get; set; }

            public string CreatedById { get; set; }  // All images have a user - at least who uploaded it.

            public DateTimeOffset? CreatedDate { get; set; }   

            public string FileExtension { get; set; }

            public string Title { get; set; }

            public string Text { get; set; }

            public string ExternalUrl { get; set; }

            public string LocalFileName { get; set; }

            public string FileType { get; set; }

            //Only in the DTO
            public byte[] FileContent { get; set; }
        }

我有一项服务可以将更新的 'Object' 发送到服务器:

            public async Task<HttpResponseMessage> UpdateObject(ObjectDTO object)
            {
                return await Http.PostAsJsonAsync(Routes.UpdateObjectRoute, object);
            }

由于这可能非常大,我希望能够向用户显示进度条。在 PostAsJsonAsync 函数中是否有 'simple' 获取进度信息的方法,或者我是否正在接近这个问题......我已经看到上传单个文件的示例,但我更愿意将数据放在一起。

我通过采用不同的方法解决了这个问题,并在所有情况下都直接从客户端上传到 Azure。我把它放在那里是为了帮助其他遇到同样问题的人。

为此,我创建了服务器 api 调用来处理我可以来回传递的 'ExternalFileDTO' 对象。 调用服务器以获取上传 SAS 密钥、提供上传 URI 并在此过程中创建数据库记录。

文件直接上传到azure,然后对每个文件再次调用服务器验证完成。

在这里非常感谢 Andrew Hoefling 的博客:Upload Large Files in C#

此外,通过浏览文档,我想出了如何设置 iProgress 处理程序。

代码如下:

       public long maxFileSize = 2000000000;
    const int BufferSize = 1000000;
    const int maxAllowedFiles = 5;
    //string AllowedFileTypes = String.Join(",", Permitted.Extensions);
    public string AllowedFileTypes = string.Join(",", FileExtensions.ValidExtensionsAll);

    public async Task<InfoBool> HandleFilesUpload(FileChangedEventArgs e, IProgress<Tuple<int, int, string>> progressHandler,
        IProgress<Tuple<int,string>> fileCountHandler, ExternalFileDTO fileTemplate, InfoBool isCancelling)
    {

        int FileCount =0;
        fileTemplate.CreatedBy = _userservice.CurrentUser;
        Tuple<int, string> reportfile;
        Tuple<int, int, string> CountProgressName;
        foreach (var file in e.Files)
        {
            FileCount++;
            reportfile = Tuple.Create(FileCount, file.Name);

            fileCountHandler.Report(reportfile);
            try
            {
                if (file == null)
                {
                    return new InfoBool(false, "File is null");

                }
                long filesize = file.Size;
                if (filesize > maxFileSize)
                {
                    return new InfoBool(false, "File exceeds Max Size");

                }
                fileTemplate.OriginalFileName = file.Name;

                var sendfile = await _azureservice.GetAzureUploadURLFile(fileTemplate);
                if (!sendfile.Status.Success) // There was an error so return the details
                {
                    return sendfile.Status;
                }
                CurrentFiles.Add(sendfile); // Add the returned sendfile object to the list
                BlockBlobClient blockBlobclient = new BlockBlobClient(sendfile.CloudURI);

                byte[] buffer = new byte[BufferSize];
                using (var bufferedStream = new BufferedStream(file.OpenReadStream(maxFileSize), BufferSize))
                {
                    int readCount = 0;
                    int bytesRead;
                    long TotalBytesSent = 0;
                    // track the current block number as the code iterates through the file
                    int blockNumber = 0;

                    // Create list to track blockIds, it will be needed after the loop
                    List<string> blockList = new List<string>();

                    while ((bytesRead = await bufferedStream.ReadAsync(buffer, 0, BufferSize)) > 0)
                    {
                        blockNumber++;
                        // set block ID as a string and convert it to Base64 which is the required format
                        string blockId = $"{blockNumber:0000000}";
                        string base64BlockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId));

                        Console.WriteLine($"Read:{readCount++} {bytesRead / (double)BufferSize} MB");

                        // Do work on the block of data
                        await blockBlobclient.StageBlockAsync(base64BlockId, new MemoryStream(buffer, 0, bytesRead));
                        // add the current blockId into our list
                        blockList.Add(base64BlockId);
                        TotalBytesSent += bytesRead;
                        int PercentageSent = (int)(TotalBytesSent * 100 / filesize);
                        CountProgressName = Tuple.Create(FileCount, PercentageSent, file.Name);
                        if (isCancelling.Success) //Used an InfoBool so its passed by reference
                        {
                            break; // The user has cancelled, so wind up.
                        }
                        progressHandler.Report(CountProgressName);
                    }
                    // add the blockList to the Azure which allows the resource to stick together the chunks
                    if (isCancelling.Success)
                    {
                        await blockBlobclient.DeleteIfExistsAsync(); // Delete the blob created
                    }
                    else
                    {
                        await blockBlobclient.CommitBlockListAsync(blockList);
                    }
                    // make sure to dispose the stream once your are done
                    bufferedStream.Dispose();   // Belt and braces
                }
                //
                // Now make a server API call to verify the file upload was successful
                //
                // Set the file status to the cancelling, cos the server can delete the file.
                sendfile.Status = isCancelling;
                await _azureservice.VerifyFileAsync(sendfile);

                if (isCancelling.Success)
                {
                    break;      // This breaks out of the foreach loop
                }

            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
            }
            finally
            {

            }
        }
        return new InfoBool(true, "All Ok");

    }

... 这是来自 Blazor UI 线程页面的代码:

        public InfoBool IsCancelling = new InfoBool(false, "Not Cancelling"); // To allow cancel of upload

        protected override void OnInitialized()
    {
        progressHandler = new Progress<Tuple<int, int, string>>(UploadProgressChanged);
        fileCountHandler = new Progress<Tuple<int,string>>(FileCountChanged);
        FileService.CurrentFilesChanged += Refresh; // Refresh the page on the change of files
    }

    private void FileCountChanged(Tuple<int,string> FileNoAndNameSending)
    {
        Console.WriteLine($"FileCount Changed  = {FileNoAndNameSending}");
        FileCount = FileNoAndNameSending.Item1;
        FileNameUploading = FileNoAndNameSending.Item2;
        this.StateHasChanged();
    }

    private void UploadProgressChanged(Tuple<int, int, string> CountProgressName)
    {
        Console.WriteLine($"File Name: {CountProgressName.Item3} /n Fileno: {CountProgressName.Item1}, Upload Progress Changed Percentage = {CountProgressName.Item2}");
        FileCount = CountProgressName.Item1;
        ProgressPercent = CountProgressName.Item2;
        FileNameUploading = CountProgressName.Item3;
        if (FileCount >= TotalFilesUploading && ProgressPercent >=100)
        {
            // This is the last file and it is complete
            Console.WriteLine($"Last File reached at 100%");
            IsUploading = false;
            // UploadingComplete = true; // Set the finished flag
            HideModal();    //File has finished uploading close and refresh
            OnUserFilesUploaded.InvokeAsync();
        }
        // ShowStatusMsg($"Uploading no {FileCount} - {FileNameUploading} -{ProgressPercent}% Complete");
        this.StateHasChanged();
    }

我目前正在整理 'isCancelling',但它确实被传递到主循环并取消了它。我在删除 blockBlob 时遇到问题(此站点上的另一个问题)。

总之,祝你工作顺利……确实如此。