Listing zip file content in Blazor
This is the first post in a series on working with dotnet projects directly in your browser.
- Part 1 - Listing zip file content in Blazor
- Part 2 - Modifiying the content of the zip file in Blazor using a rich text editor in a Flux/Redux architecture
- Part 3 - Running a console application directly in your browser
Introduction
Zip files are ubiquitous these days and are one of the (if not the) de facto ways to simultaneously send a collection of files altogether.
In this post, we’ll see how to enumerate and display zip file content in a Blazor WASM application.
The solution
Here is a simple way to achieve this using a flattening the zip structure:
Index.razor
On this page, we have an input file component that accepts zip file.
@page "/"
<h1>Hello, world!</h1>
<div class="drag-drop-zone">
<InputFile OnChange="OnInputFileChange" accept=".zip" />
@_status
</div>
@if (_fileName != null)
{
<h3>@_fileName</h3>
if (_entries.Any())
{
<ul>
@foreach (var item in _entries.Where(x => !x.Name.EndsWith("/")))
{
<li>
<h4>@item.Name</h4>
<pre>@item.Content</pre>
</li>
}
</ul>
}
}
Once the file has been “uploaded” it’s content will be displayed in a list.
Index.razor.cs
This is the backend of the Index.razor
page. On file upload, pass the file stream to the ZipService
.
public partial class Index
{
private const string DefaultStatus = "Choose a zip file";
private List<ZipEntry> _entries;
private string _fileName;
private string _status = DefaultStatus;
[Inject] private ZipService ZipService { get; set; }
private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
await using var stream = e.File.OpenReadStream();
_entries = await ZipService.ExtractFiles(stream);
_fileName = e.File.Name;
_status = DefaultStatus;
}
}
ZipEntry.cs
This is the model we’ll be using to represent the zip file entries.
public record ZipEntry
{
public string Name { get; init; }
public string Content { get; init; }
}
ZipService.cs
The ZipService
class reads a zip file stream and returns a list of ZipEntry
.
public class ZipService
{
public async Task<List<ZipEntry>> ExtractFiles(Stream fileData)
{
await using var ms = new MemoryStream();
await fileData.CopyToAsync(ms);
using var archive = new ZipArchive(ms);
var entries = new List<ZipEntry>();
foreach (var entry in archive.Entries)
{
await using var fileStream = entry.Open();
var fileBytes = await fileStream.ReadFully();
var content = Encoding.UTF8.GetString(fileBytes);
entries.Add(new ZipEntry { Name = entry.FullName, Content = content });
}
return entries;
}
}
public static class StreamExtension
{
public static async Task<byte[]> ReadFully(this Stream input)
{
await using var ms = new MemoryStream();
await input.CopyToAsync(ms);
return ms.ToArray();
}
}
Remember to register your service
Let’s register our ZipService
in the Program
class.
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(
sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<ZipService>();
await builder.Build().RunAsync();
}
}
Conclusion
Here is a demo of the finished result:
Understandably, this won’t work with encrypted files.
The code covered in this blog post is available here:
blog-examples/tree/master/listing-zip-file-content-in-blazor