使用 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) 的类函数

为什么说Babel[?be?bl]将推动JavaScript的发展

asp.net调用javascript返回值

如何强制解锁小米bl锁 联发科

不解BL锁能不能直接刷机

位置无关码(BL)与绝对位置码(LDR)