获取进度 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 时遇到问题(此站点上的另一个问题)。
总之,祝你工作顺利……确实如此。
我正在我的 (.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 时遇到问题(此站点上的另一个问题)。
总之,祝你工作顺利……确实如此。