从 Blazor 服务器中的流下载失败

Ste*_*e W 4 c# closedxml blazor-server-side .net-6.0

在尝试使用 ClosedXML 构建 Excel 文件时,我遇到了一些有关内存流的意外行为。在调试器中,我可以看到 MemoryStream ms 正在由该wb.SaveAs()函数填充。但是,当直接将其传递到 时DotNetStreamReference,该文件将作为 0 字节的 blob 下载。

奇怪的是,当我将流转换为字节数组并返回流时,该函数按预期工作。

我在这里错过了一些基本的东西吗?

这是使用MS 示例得出的

技术:

  • Blazor 服务器 .Net 6
  • 封闭XML 0.96

C# 代码

async Task ExportToExcel()
{
    string fileName = $"DocumentsExport{DateTime.UtcNow.Ticks}.xlsx";
    var wb = new ClosedXML.Excel.XLWorkbook();
    var ws = wb.AddWorksheet("Documents");

    // construct headers
    ws.Cell(1, 1).SetValue<string>("Document Name");
    ws.Cell(1, 2).SetValue<string>("Description");
    ws.Cell(1, 3).SetValue<string>("Sort Order");
    ws.Cell(1, 4).SetValue<string>("Category Name");
    ws.Cell(1, 5).SetValue<string>("Group Name");
    ws.Cell(1, 6).SetValue<string>("Date Modified");

    // construct worksheet contents
    for (int i = 0; i < documents.Count; i++)
    {
        var document = documents[i];
        int rowIndex = i + 2;
        ws.Cell(rowIndex, 1).SetValue<string>(document.Name);
        ws.Cell(rowIndex, 2).SetValue<string?>(document.Description);
        ws.Cell(rowIndex, 3).SetValue<int?>(document.SortOrder);
        ws.Cell(rowIndex, 4).SetValue<string>(document.DocumentCategory.Name);
        ws.Cell(rowIndex, 5).SetValue<string>(document.DocumentCategory.DocumentGroup.Name);
        ws.Cell(rowIndex, 6).SetValue<DateTime?>(document.DateModified);
    }

    // apply formatting and filters
    ws.RangeUsed().SetAutoFilter();
    ws.Columns().AdjustToContents();

    // save file and convert to byte array
    // passing this stream to the stream reference causes the file to be downloaded with 0 bytes, thus no content
    using MemoryStream ms = new MemoryStream();
    wb.SaveAs(ms);

    // this line fails
    //using var streamRef = new DotNetStreamReference(stream: ms);

    // this line works
    using var streamRef = new DotNetStreamReference(stream: new MemoryStream(ms.ToArray()));

    // execute javaScript to download file
    await JS.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
}
Run Code Online (Sandbox Code Playgroud)

JavaScript 代码:

<script>
// script function used to download small files, like excel reports
// https://learn.microsoft.com/en-us/aspnet/core/blazor/file-downloads?view=aspnetcore-6.0
window.downloadFileFromStream = async (fileName, contentStreamReference) => {
    const arrayBuffer = await contentStreamReference.arrayBuffer();
    const blob = new Blob([arrayBuffer]);
    const url = URL.createObjectURL(blob);
    const anchorElement = document.createElement('a');
    anchorElement.href = url;
    anchorElement.download = fileName ?? '';
    anchorElement.click();
    anchorElement.remove();
    URL.revokeObjectURL(url);
}
Run Code Online (Sandbox Code Playgroud)

Mag*_*ron 8

您必须重置流位置。保存后,流位置位于流的末尾,当您传递该流时,其他方法从当前位置开始读取,因此没有任何内容可读取。ToArray从起始位置开始转换整个流,这就是为什么您可以看到所有数据。当您从该数组创建一个新数组时MemoryStream,它将拥有所有数据,但它的位置将位于开头,这就是它起作用的原因。

所以,你所要做的就是

using MemoryStream ms = new MemoryStream();
wb.SaveAs(ms);
ms.Position=0;
Run Code Online (Sandbox Code Playgroud)

它应该有效。