使用 C#(无 JavaScript)直接将 Blazor WebAssembly 文件分块到 Azure Blob 存储
Posted
技术标签:
【中文标题】使用 C#(无 JavaScript)直接将 Blazor WebAssembly 文件分块到 Azure Blob 存储【英文标题】:Blazor WebAssembly File Chunking directly to Azure Blob Storage using C# (no JavaScript) 【发布时间】:2021-11-04 11:02:30 【问题描述】:我正在使用 Blazor Webssembly 和 .Net 5.0。我需要能够使用分块将非常大的文件 (2-5GB) 上传到 Azure Blob 存储,方法是分阶段上传文件数据,然后在暂存所有块后在 blob 上触发最终提交消息。
我能够使用 SharedAccessSignatures 和 Azure javascript 库来实现这一点(网上有很多示例)。
但是我想使用纯 C# 来处理这个问题。我遇到的问题是 IBrowserFile 引用似乎试图将整个文件加载到内存中,而不是仅读取循环中每个阶段所需的块。
为简单起见,下面的示例代码不包含任何 Azure Blob 存储代码。我只是将分块和提交消息写入控制台:
@page "/"
<InputFile OnChange="OnInputFileChange" />
@code
async Task OnInputFileChange(InputFileChangeEventArgs e)
try
var file = e.File;
int blockSize = 1 * 1024 * 1024;//1 MB Block
int offset = 0;
int counter = 0;
List<string> blockIds = new List<string>();
using (var fs = file.OpenReadStream(5000000000)) //<-- Need to go up to 5GB
var bytesRemaining = fs.Length;
do
var dataToRead = Math.Min(bytesRemaining, blockSize);
byte[] data = new byte[dataToRead];
var dataRead = fs.Read(data, offset, (int)dataToRead);
bytesRemaining -= dataRead;
if (dataRead > 0)
var blockId = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(counter.ToString("d6")));
Console.WriteLine($"blockId:blockId");
Console.WriteLine(string.Format("Block 0 uploaded successfully.", counter.ToString("d6")));
blockIds.Add(blockId);
counter++;
while (bytesRemaining > 0);
Console.WriteLine("All blocks uploaded. Now committing block list.");
Console.WriteLine("Blob uploaded successfully!");
catch (Exception ex)
Console.WriteLine(ex.Message);
第一个问题是:
不支持同步读取。
所以我尝试了:
var fs = new System.IO.MemoryStream();
await file.OpenReadStream(5000000000).CopyToAsync(fs);
using (fs)
...
但显然我现在会遇到内存问题!我愿意。即使是 200kb 文件的错误是:
内存不足
还有超过 1MB 的内容:
垃圾收集器无法为主要堆部分分配 16384u 字节的内存。
有没有办法从 IBrowserFile 一次读取较小的数据块,这样就可以在客户端 Blazor 中本地实现,而无需求助于 JavaScript?
【问题讨论】:
【参考方案1】:.NET 6.0 有一个很好的 Stream.CopyToAsync()
实现,可以在这里找到
https://github.com/microsoft/referencesource/blob/master/mscorlib/system/io/stream.cs
这会将数据从一个流异步复制到另一个流。
它的要点是这样的:
private async Task CopyToAsyncInternal(Stream source, Stream destination, Int32 bufferSize, CancellationToken cancellationToken)
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
(从上面的链接复制)
将bufferSize
设置为 4096 或倍数,它应该可以工作。其他值也是可能的,但通常块被视为 4k 的倍数。
这里的假设是您有一个可写流,您可以将字节异步写入该流。您可以修改此循环以计算块数和每个块的其他内容。在任何情况下都不要使用内存流客户端或服务器端来处理大文件。
【讨论】:
以上是关于使用 C#(无 JavaScript)直接将 Blazor WebAssembly 文件分块到 Azure Blob 存储的主要内容,如果未能解决你的问题,请参考以下文章
如何使用来自客户端 C# Winforms 的 asmx 服务 (BL) 的类函数