简体   繁体   中英

How do I upload files with Blazor?

I found the BlazorInputFile library, but there are still-open PRs from October of 2019, and I am not sure whether this library is still maintained. Also, I found a couple articles in blogs about how we can upload files with JS in Blazor. While I don't want to use JS if possible, I do need to upload files using Blazor... is it possible to do so without using JavaScript?

Answering this question for those like me who are still searching the best way to upload files in Blazor. I too was dabbling with installing SteveSandersonMS' repo and then realised that as of February 2021 there is actually a native InputFile component in ASP.NET Core 5.0.

It supports uploading of single and multiple files in Blazor and is easy to use. (And you don't need to add your own js files etc.)

I used it for single file uploads - all you need to do is add the InputFile component in the Razor page:

<InputFile OnChange="@SingleUpload" />

and then in my case I needed the file in a byte array:

@code {

   private async Task SingleUpload(InputFileChangeEventArgs e)
   {
       MemoryStream ms = new MemoryStream();
       await e.File.OpenReadStream().CopyToAsync(ms);
       var bytes = ms.ToArray();
       //do something with bytes
   }
}

InputFileChangeEventArgs gives you an IReadOnlyList of IBrowserFile which you can use to get the Name , LastModified , Size and ContentType , as well as an OpenReadStream method for getting a Stream .

Hopefully this helps, there is good documentation and code on how to get multiple files in the ASP.NET docs.

Edit: You will also need to add the System.IO namespace:

@using Sytem.IO

, thanks nogood.

As of June 2020, The best method (WA), assuming you are using a form is to use a Tewr's FileReader. Let start with the API, the post controller would be :

      public async Task<IActionResult> PostMedia(
        [FromForm] IFormFile Picture,
        [FromForm] string Focus,
        [FromForm] string ID,
        [FromForm] string Title,
        [FromForm] string FormType,
        [FromForm] string AnimalType,
        [FromForm] string Mode,
        [FromForm] string AnimalID
        )
    {
        Debug.WriteLine($"-------------------------------------{Focus}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{ID}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{Title}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{FormType}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{AnimalType}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{Mode}-----------------------------------------------");
        Debug.WriteLine($"-------------------------------------{AnimalID}-----------------------------------------------");


        //check if file was fully uploaded
        if (Picture.Length == 0 || Picture == null)

            return BadRequest("Upload a new File");
        else
            return Ok ("do something with this data....") 
     }

Then the post method on the client side would be:

    public async Task PostFile()
  {
    //create content headers
    var content = new MultipartFormDataContent();
    content.Headers.ContentDisposition = new 
    System.Net.Http.Headers.ContentDispositionHeaderValue("form-data");

    //create content
    content.Add(new StreamContent(Pic.Stream, (int)Pic.Stream.Length), "Picture", Pic.FileName);
    content.Add(new StringContent(Pic.Title), "Title");
    content.Add(new StringContent(Pic.Focus), "Focus");
    content.Add(new StringContent(Pic.ID), "ID");
    content.Add(new StringContent(Pic.FormType), "FormType");
    content.Add(new StringContent(Pic.AnimalType), "AnimalType");
    content.Add(new StringContent(Pic.Mode), "Mode");
    content.Add(new StringContent(Pic.AnimalID), "AnimalID");
    //call to the server
    var upload = await Http.PostAsync("Media",content);

    //get server response
    Pic.Message = await upload.Content.ReadAsStringAsync();
   }

Tewr File reader helps you read the file into a stream which in my case is passed to the Pic object. The reading function which is binded to the onchange of your input element in the form would be :

  public async Task ReadFile()
   {
    var file = (await fileReaderService.CreateReference(Xelement).EnumerateFilesAsync()).FirstOrDefault();

    if (file == null)  return;


    var fileInfo = await file.ReadFileInfoAsync();

    Pic.FileName = fileInfo.Name;


    // Read into RAM
    using (var memoryStream = await file.CreateMemoryStreamAsync((int)fileInfo.Size))
    {
        // Copy store image into pic object
        Pic.Stream = new MemoryStream(memoryStream.ToArray());
    }

}

Note that Xelement is ElementReference, and it used as ref on the input element in the form.

I do this by using a component and some javascript (looks like a button). Once the component and js are incorporated, you never have to worry about it again...

Here's the Upload Component (Upload.Razor):

 @inject IJSRuntime JSRuntime @if (AllowMulitple) { <input id="Xinputfile00" type="file" accept="@Filter" @onchange="UploadFile" multiple hidden /> } else { <input id="Xinputfile00" type="file" accept="@Filter" @onchange="UploadFile" hidden /> } <button class="btn btn-default" @onclick="ClickUpload">@Title</button> @code { [Parameter] public FileData[] Files { get; set; } [Parameter] public string Filter { get; set; } [Parameter] public string Title { get; set; } [Parameter] public bool AllowMulitple { get; set; } [Parameter] public Action Uploaded { get; set; } async Task UploadFile() { string[] result = await JSRuntime.InvokeAsync<string[]>("blazorExtensions.GetFileData", "Xinputfile00"); List<FileData> results = new List<FileData>(); foreach (string file in result) { results.Add(new FileData(file)); } this.Files = results.ToArray(); if (Uploaded != null) { Uploaded(); } } async Task ClickUpload() { await JSRuntime.InvokeVoidAsync("blazorExtensions.InvokeClick", "Xinputfile00"); } public class FileData { public string Base64 { get; set; } public string MIMEType { get; set; } public byte[] Bytes { get { return Convert.FromBase64String(this.Base64); } } public FileData(string data) { if (string.IsNullOrWhiteSpace(data) || !data.Contains(",")) { return; } string[] alldata = data.Split(','); this.MIMEType = alldata[0].Remove(0, 5).Replace(";base64", ""); this.Base64 = alldata[1]; } }

Here's the javascript excerpt:

 window.blazorExtensions = { GetFileData: async function (id) { var target = document.getElementById(id); var filesArray = Array.prototype.slice.call(target.files); return Promise.all(filesArray.map(window.blazorExtensions.fileToDataURL)); }, fileToDataURL: async function (file) { var reader = new FileReader(); return new Promise(function (resolve, reject) { reader.onerror = function () { reader.abort(); reject(new DOMException('Error occurred reading file ' + file)); }; reader.onload = function (event) { resolve(reader.result); console.log('resolved'); }; reader.readAsDataURL(file); console.log('returned'); }) }, InvokeClick: function (id) { var elem = document.getElementById(id); if (typeof elem.onclick == "function") { elem.onclick.apply(elem); } elem.click(); }, }

And here's a calling markup sample:

 <Upload @ref="upload" Filter=".xlsx" Title="Upload" AllowMulitple="false" Uploaded="DoMyExcelThingOrSomething" />

and the method it calls after upload:

    Upload upload;
    void DoMyExcelThingOrSomething()
{
    if (upload.Files.Length < 1 || string.IsNullOrWhiteSpace(upload.Files[0].Base64))
    {
        //...nothing good here...
        return;
    }
    //play with upload.Files here...
}

At the current state of affairs (as 2 April 2020), you will require JS, it is inevitable.

There are two main approaches you can take:

  • get the file data in the onchange event of the input, and call C# methods by passing the byte[] to them - that's basically the file selector approach you linked where you get the file data in the Blazor app to do whatever you want with it.

  • get the file data in the onchange event of the input, and use JS to call a remote endpoint that will receive the file and do something with it (like save it on your NAS or put it in your DB). This one is an actual file upload, as opposed to a file selector.

Both approaches are similar from coding perspective - you need JS. Perhaps in a future version of Blazor we will get an <InputFile> that will do the selection so you can to uploads with C# HTTP requests.

The File Selector approach is relatively easy to implement (literally a few lines), but it does not give you a file on the server, you have to work for it a little. The File Upload approach is harder to get right. I would personally use someone else's package for either. For file uploads things like Telerik UI for Blazor can be a commercial fit, and for the simpler selectors there is already another answer that links examples. By the way, Telerik's demos also have one such example as a component implemented for some of the demos.

For Blazor Server, the following would upload the file to the server. There's no need to have a separate API server, or to use JS code. And it converts the stream into a file.

@using System.IO
@inject IWebHostEnvironment env

@*for ibrowser*@
@using Microsoft.AspNetCore.Components.Forms;

<h1>Blazor Server File Upload</h1>

<h3>@Message</h3>

<form @onsubmit="OnSubmit">
    <InputFile OnChange="OnInputFileChange" multiple />
    <br /><br />
    <button type="submit">Upload Selected File(s)</button>
</form>

@code {
    string Message = "No file(s) selected";
    IReadOnlyList<IBrowserFile> selectedFiles;

    void OnInputFileChange(InputFileChangeEventArgs e)
    {
        selectedFiles = e.GetMultipleFiles();
        Message = $"{selectedFiles.Count} file(s) selected";
        this.StateHasChanged();
    }

    async void OnSubmit()
    {
        foreach (var file in selectedFiles)
        {
            Stream stream = file.OpenReadStream();
            var path = $"{env.WebRootPath}\\{file.Name}";
            FileStream fs = File.Create(path);
            await stream.CopyToAsync(fs);
            stream.Close();
            fs.Close();
        }
        Message = $"{selectedFiles.Count} file(s)   uploaded on server";
        this.StateHasChanged();
    }
}

(Minor edit of http://www.bipinjoshi.net/articles/06473cc7-a391-409e-948d-3752ba3b4a6c.aspx )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM