WebAPI2CLI
This is a part of the series where about how I made the WebAPI2CLI - Execute ASP.NET Core WebAPI from Command Line
Source code on https://github.com/ignatandrei/webAPI2CLI/
I realized that I should have somehow let the user play with the application from his PC. How can I do that , if the application is on some site ? Answer: download the whole site as a zip file.
It should be relatively easy for the user– so an endpoint routing should be used – something like “/zip”
It should be relatively easy for the programmer – so I figured
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapZip(app); // IApplicationBuilder app
});
The code is also relativeley simple: enumerating recursive file and folders , creating the zip archive in memory, sending to the user
public static void MapZip(this IEndpointRouteBuilder endpoints, IApplicationBuilder app)
{
//see more at
//https://andrewlock.net/converting-a-terminal-middleware-to-endpoint-routing-in-aspnetcore-3/
endpoints.Map(“/zip”, async context =>
{
var response = context.Response;
var name = Assembly.GetEntryAssembly().GetName().Name + “.zip”;
response.ContentType = “application/octet-stream”;
var b = GetZip(app.ApplicationServices.GetService<IWebHostEnvironment>());
//https://github.com/dotnet/aspnetcore/blob/master/src/Mvc/Mvc.Core/src/Infrastructure/FileResultExecutorBase.cs
var contentDisposition = new Microsoft.Net.Http.Headers.ContentDispositionHeaderValue(“attachment”);
contentDisposition.SetHttpFileName(name);
response.Headers[HeaderNames.ContentDisposition] = contentDisposition.ToString();
await response.Body.WriteAsync(b);
});
}
private static Memory<byte> GetZip(IWebHostEnvironment env)
{
//var b = new Memory<byte>(Encoding.ASCII.GetBytes($”{env.ContentRootPath}”));
var firstDir = new DirectoryInfo(env.ContentRootPath);
var nameLength = firstDir.FullName.Length + 1;
using var memoryStream = new MemoryStream();
using var zipToOpen = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
foreach (FileInfo file in firstDir.RecursiveFilesAndFolders().Where(o => o is FileInfo).Cast<FileInfo>())
{
var relPath = file.FullName.Substring(nameLength);
var readmeEntry = zipToOpen.CreateEntryFromFile(file.FullName, relPath);
}
zipToOpen.Dispose();
var b = new Memory<byte>(memoryStream.ToArray());
return b;
}
private static IEnumerable<FileSystemInfo> RecursiveFilesAndFolders(this DirectoryInfo dir)
{
foreach (var f in dir.GetFiles())
yield return f;
foreach (var d in dir.GetDirectories())
{
yield return d;
foreach (var o in RecursiveFilesAndFolders(d))
yield return o;
}
}
Some points to be noted:
1. It will work only if the site and user have the same operating system( assuming you have did self contained the .NET Core application)
2. ContentDispositionHeaderValue – exists in 2 libraries
3. I have shameless copy code from StackOverflow and github aspnet
4. I did not put in a separate library, nor I do automatic tests. Sorry, too tired
5. It works only if you put
endpoints.MapZip(app); // IApplicationBuilder app
If not, it does not zip!
6. IEndpointRouteBuilder – it is defined only you have the library .net core 3, not .net standard 2
<TargetFramework>netcoreapp3.1</TargetFramework>
<ItemGroup>
<FrameworkReference Include=”Microsoft.AspNetCore.App” />
</ItemGroup>