Blazor 5 File Upload Performance

Wenn Sie die Wahl hätten: Die nächste Urlaubsreise an die italienische Adria, mit dem SUV bei gemächlichen 140 oder mit einem Kinder Elektro Porsche so mit ca 2km/h, was würden sie wählen?

Sie werden es nicht glauben Blazor fährt mit 2km/h. Weil das Ergebnis so unglaublich ist, habe ich die Testcase online gestellt https://blazorapp5server20200926110727.azurewebsites.net/upload und /up

(Link nur vorübergehend)

Das Problem: Datei Upload ist für Blazor bisher ein veritables Problem. Es beginnt strukturell. Der Flow ist völlig Unterschiedlich bei Webassembly oder Server App. Im ersten Fall “liegt” die Datei im Memory des Browsers und muss erst mit irgendeiner Art von Service rüber transportiert werden. Bei der Server seitigen Lösung ist die Datei zwar am Server und kann mit einem 3 Zeiler auf die Platte geschrieben werden. Aber wie kommt sie dorthin? Über die SignalR Verbindung, die eine max. Blockgröße von 32KB aufweist. Dies lässt schlimmes erahnen. Wobei Microsoft ja nun mit Blazor 5 eine InputFile Componente mitliefert. Muss doch super sein oder?

Zunächst ein simpler Datei Upload mit ASP.NET 5 Razor View Engine. Das kann man auch in ein Blazor Projekt einbauen und sozusagen mischen. Alle Tests mit Standard IISExpress

   1:   <form enctype="multipart/form-data" method="post">
   2:     <input type="file" asp-for="Datei"  name="Datei"/>
   3:     <input type="submit" />
   4:  </form>
   5:  @Model.Zeit

Das ist sozusagen HTML Standard + Model Bindung aus Razor. Kleiner Hinweis: normalerweise würde ich das Name Attribut nicht mit dem Wert “Datei” setzen, da Blazor das per asp-for Binding selbst tut . Es scheint aber bei einem derartigen Minimal Formular nach dem Post das FileUpload Obejct null zu sein, wenn nicht belegt.

   1:   [IgnoreAntiforgeryToken(Order = 1001)]
   2:   public class uploadModel : PageModel
   3:   {
   4:   public long Zeit { get; set; }
   5:   private IWebHostEnvironment _environment;
   6:   public uploadModel(IWebHostEnvironment environment)
   7:          {
   8:          _environment = environment;
   9:          }
  10:   
  11:   [BindProperty]
  12:   public IFormFile Datei { get; set; }
  13:   
  14:  public async Task OnPostAsync()
  15:    {
  16:     var file = Path.Combine(_environment.ContentRootPath,
"wwwroot/upload", Datei.FileName);
  17:      Stopwatch watch = new Stopwatch();
  18:      watch.Start();
  19:      using (var fileStream = new FileStream(file, FileMode.Create))
  20:        {
  21:        await Datei.CopyToAsync(fileStream);
  22:         }
  23:     watch.Stop();
  24:    Zeit=watch.ElapsedMilliseconds;
  25:   }
  26:      }

Auch hier ein zwei Tricks. Wenn das Anti Forgery Token nicht gesetzt ist, erhalten sie einen 400 Fehlermeldung. Der Pfad von der App wird aus Environment extrahiert und um das eigentliche Root Verzeichnis wwwroot ergänzt. Alles was dort liegt ist nicht ausführbar.

Vermutlich wird der erste Test mit einer Fehlermeldung enden.

HTTP Error 413.1 - Request Entity Too Large

Abhilfe schafft eine web.config mit folgenden Inhalt

   1:  <?xml version="1.0" encoding="utf-8"?>
   2:  <configuration>
   3:    <system.webServer>
   4:        <security>
   5:            <requestFiltering >
   6:                <requestLimits 
maxAllowedContentLength="1000000000"></requestLimits>
   7:            </requestFiltering>
   8:        </security>
   9:       </system.webServer>
  10:  </configuration>

Die länge der Bytes obliegt ihren Befindlichkeiten.

Als nächstes wieder im Pages Verzeichnis die Blazor Komponente mit Route als Page. Minimaler HTML Code, allerdings so auch mit mehreren Dateien gleichzeitig möglich.

   1:  @page "/up"
   2:  @using System.Threading
   3:  @using System.IO
   4:  @using System.Diagnostics;
   5:  @using Microsoft.AspNetCore.Hosting;
   6:  @inject IWebHostEnvironment environment
   7:  <InputFile OnChange="@OnInputFileChange" multiple />
   8:  @Zeit

Für den folgenden Part gibt es noch keine Dokumentation. Ich musst also aus dem Github Code Doc mir die Sache zusammenreimen. Dabei bin ich einem Irrtum aufgesessen, der auch bei Visual Studio Tooltip sichtbar wird. Der Default Wert ist 500Kb kann aber nach oben beliebig vergrößert werden.

Screenshot 2020-09-26 120049

   1:   private async Task OnInputFileChange(InputFileChangeEventArgs e)
   2:  {
   3:      var Dateien = e.GetMultipleFiles();
   4:      foreach (var Datei in Dateien)
   5:       {
   6:       CancellationToken cancellationToken = default;
   7:       Stopwatch watch = new Stopwatch();
   8:       watch.Start();
   9:       var file= Datei.OpenReadStream(
5120000000, cancellationToken);
  10:       var pfad= Path.Combine(environment.ContentRootPath, 
"wwwroot/upload", Datei.Name); //Fielname vs Name
  11:       using (var datei = File.Create(pfad))
  12:        {
  13:            await file.CopyToAsync(datei);
  14:         }
  15:        watch.Stop();
  16:        Zeit =watch.ElapsedMilliseconds;
  17:  }
  18:      }

Kommen wir zum Tretroller Tempo. Edge Browser.

Lokal (40MB):  486ms Form Post, 11017ms mit Blazor Inputfile = Faktor 23 Unterschied
Azure (800KB) : 116ms Form Post, 7978 Blazor Inputfile= Faktor 70 Unterschied
Azure (40MB) : 2426ms Form Post, 351161 Blazor Inputfile= Faktor 145 Unterschied (Chrome -Kein Upload mit Edge möglich)

Laut Netzwerk Performance Anzeige bei 1,4Mbits  bei Blazor Inputfile und 16 bei Form Post.

Kommentare sind geschlossen