Upload a zip file from the client desktop to the server and unzip it. The methodology can be used with any file whether text or binary. It resolves an issue with the method as posted elsewhere.

The GetListofSamples app requires a suite of sample C# .NET Core apps in folders on the Server. This can be located anywhere on the server. But for remote deployment such as Azure Web Apps, there is a need to be able to upload the samples to the server. This post discusses the Post of a Zip file from the Blazor Client, a WASM app, to its Blazor Server, as well as the extraction of the file contents. The mechanism used relies on two previous posts. One for getting a file from the client desktop into the client app and one for Posting that content to the server. The latter method as posted by the author does not work properly, especially for binary content as it inserts numerous null characters into the upload stream. The methodology used here has resolved that issue.

Summary

Page Name: Pages/Uploads.razor
Purpose: Load a collection of .NET Core C# sample apps from the client desktop as a Zip file and upload it to the server. Extract the file contents upon upload and scan the sample folder there.
Code: Uploads.razor In djaus2/GetSampleApps on GitHub
Component Used InputFile
Component Reference File uploads with Blazor … Steve Sanderson
Example of Usage <InputFile OnChange="HandleSelection" />
Explanation of Example: Faciltates the selection of a desktop file by the client and reading of the file contents into the client app.
Nuget Package for InputFile Install-Package BlazorInputFile
Upload Reference: Uploading Files In Blazor … Mike Brind
Comment This makes use of the InputFile Component to load the file contents in the client then upload it. It inserts null bytes that corrupts the format of binary files such as image and zip files.
Microsoft Docs:  

The Issue

The Mike Brind post implements the file upload from the InputFile selection as mooted as a possibility in Steve Sanderson’s post about the control. Mike uses the following code to create the buffer:

var buf = ms.GetBuffer();

Unfortunately that buffer that is padded out with nulls. For example a short text file becomes a 256 byte frame with numerous training null characters. For a text file, that is not an issue as they tend to get ignore by text editors or, at least, the text is rendered OK with blanks on the end. For a binary file where layout of the bytes is important, additional null bytes corrupts the information. For example, a Zip file of about 2k byes can becomes 8K+ bytes. The solution used here is to read the Memory Stream created by reading the file, byte for byte up to the number of bytes in the stream, into a byte array the size of the stream.

Client Post

Handle file selection

After selecting the file using the InputFile control, read the file into a Memory Stream.

    async Task HandleSelection(IFileListEntry[] files)
    {
        var file = files.FirstOrDefault();
        if (file != null)
        {
            try
            {
                var ms = new MemoryStream();
                await file.Data.CopyToAsync(ms, token);

                // Get Memory Stream into Byte Array

                // Form Content to send

                // Post Content to server

                // Get updated Folder and Project data

            }
        }
    }

Get Memory Stream into a Byte Buffer

Note that previous post on this used the first line. The buffer returned has excess null bytes.

    var buf = ms.GetBuffer(); // Not used as adds nulls on end.

    byte[] buff = new byte[ms.Length];
    ms.Seek(0, SeekOrigin.Begin);
    int count = await ms.ReadAsync(buff, 0, (int)ms.Length, token);

Form the Content

Include the buffer, the string ipload and the Zip filename

    var byteContent = new ByteArrayContent(buff);
    var content = new MultipartFormDataContent {
        { byteContent, "\"upload\"", file.Name }
    };

Post the Content

Post the Content, and get the response.

    var httpResponseMessage = await client.PostAsync("upload", content, token);
    responseCode = httpResponseMessage.StatusCode.ToString();
    response = await httpResponseMessage.Content.ReadAsStringAsync();
    httpResponseMessage.EnsureSuccessStatusCode();

Server Response

There are two folders on the Server:

  • ServerUploadsFolder: Where the Zip file is placed.
  • ServerSamplesFolder: Where it is unzipped to and what is scanned.

The Server Post Handler is embedded in a Controller called Upload, hence the first parameter in client.PostAsync() above.

    [Route("[controller]")]
    [ApiController]
    public class UploadController : ControllerBase
    {
        ...
    }

The Server Post Handler

    [HttpPost]
    public async Task Post(IEnumerable<IFormFile> files)
        try
        {
            foreach (var file in files)
            {
                // Get the Zip file path as on the server
                var fileContent = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
                var fileName = Path.GetFileName(fileContent.FileName.ToString().Trim('"'));
                var physicalPath = Path.Combine(ServerUploadsFolder, fileName);
                // Save the file
                using (var fileStream = new FileStream(physicalPath, FileMode.Create))
                {
                    await file.CopyToAsync(fileStream);
                }
                //Extract it
                if (Path.GetExtension(file.FileName).ToUpper() == ".ZIP")
                {

                    ZipFile.ExtractToDirectory(physicalPath, ServerSamplesFolder);
                }
                // Return Status
                FileInfo fi = new FileInfo(physicalPath);
                Response.StatusCode = 200;
                string msg = $"File Uploaded:{fi.Name}     Size:{fi.Length }";
                await Response.WriteAsync(msg); // custom error message
            }
        }
        catch (Exception ex)
        {
            Response.StatusCode = 500;
            await Response.WriteAsync(ex.Message); // custom error message
        }
    }

Follow up by the Client

Once the client is notified that the upload and extraction went OK (Code 200) it does another parameterized Http Get. This time the command is Reload. The Filename and FolderId are ignored for that command. This causes a scan of the new content in the ServerSamplesFolder folder. The client then does an un-parameterized Http Get to the server that returns the new folder data to the client:

    string setSamplesFolder = "RELOAD~2~2";
    var result = await _SamplesClient.GetTextorTextFile(setSamplesFolder);
    var Folders = (await _SamplesClient.Get()).ToList()

Server Scan

    Startup.Setup(foldId);

The code for the function Setuo() can be viewed here


 TopicSubtopic
   
 This Category Links 
Category:Blazor Index:Blazor
  Next: > Blazor How To
<  Prev:   Blazor How To