C
C#4mo ago
Davide Dunne

Blazor Server: Cannot access a closed Stream

I'm trying to export a list of objects to a csv and then make the user download it. I'm getting an error for having the stream closed. If I remove all the using in GetFileStream I end up getting another error for Javascript not found
@using System.IO
@using CsvHelper
@using System.Globalization
@inject IJSRuntime JS
@code {
[Parameter, EditorRequired]
public IEnumerable<object>? objects { get; set; }
[Parameter, EditorRequired]
public string? FileName { get; set; }
[Parameter, EditorRequired]
public string? TextDisplay { get; set; }
private Stream GetFileStream()
{
using var memoryStream = new MemoryStream();
using var streamWriter = new StreamWriter(memoryStream);
using var csvWriter = new CsvWriter(streamWriter, culture: CultureInfo.InvariantCulture);
csvWriter.WriteRecords(objects);
return memoryStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", FileName, streamRef);
}
}
<script>
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);
}
</script>
<MudIconButton Icon="@Icons.Material.Filled.Download" Color="Color.Secondary" Size="Size.Small" aria-label="Export to CSV" OnClick="DownloadFileFromStream">@TextDisplay</MudIconButton>
@using System.IO
@using CsvHelper
@using System.Globalization
@inject IJSRuntime JS
@code {
[Parameter, EditorRequired]
public IEnumerable<object>? objects { get; set; }
[Parameter, EditorRequired]
public string? FileName { get; set; }
[Parameter, EditorRequired]
public string? TextDisplay { get; set; }
private Stream GetFileStream()
{
using var memoryStream = new MemoryStream();
using var streamWriter = new StreamWriter(memoryStream);
using var csvWriter = new CsvWriter(streamWriter, culture: CultureInfo.InvariantCulture);
csvWriter.WriteRecords(objects);
return memoryStream;
}
private async Task DownloadFileFromStream()
{
var fileStream = GetFileStream();
using var streamRef = new DotNetStreamReference(stream: fileStream);
await JS.InvokeVoidAsync("downloadFileFromStream", FileName, streamRef);
}
}
<script>
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);
}
</script>
<MudIconButton Icon="@Icons.Material.Filled.Download" Color="Color.Secondary" Size="Size.Small" aria-label="Export to CSV" OnClick="DownloadFileFromStream">@TextDisplay</MudIconButton>
1 Reply
Jimmacle
Jimmacle4mo ago
private Stream GetFileStream()
{
using var memoryStream = new MemoryStream();
using var streamWriter = new StreamWriter(memoryStream);
using var csvWriter = new CsvWriter(streamWriter, culture: CultureInfo.InvariantCulture);
csvWriter.WriteRecords(objects);
return memoryStream;
}
private Stream GetFileStream()
{
using var memoryStream = new MemoryStream();
using var streamWriter = new StreamWriter(memoryStream);
using var csvWriter = new CsvWriter(streamWriter, culture: CultureInfo.InvariantCulture);
csvWriter.WriteRecords(objects);
return memoryStream;
}
this is the problem because you're using the memory stream here, it's disposed when this method returns you need to wait to dispose it until you're done using it in the calling code i think you'll also need to use the StreamWriter constructor that lets you specify leaveOpen because otherwise it will close the underlying stream when it's disposed too also potentially seek the stream back to the beginning