code it

Martins Tech Blog

Lesbare Bytes

Das Problem ist einfach umrissen: Man hat die Größe einer Datei oder (auch gern genommen) eine Größenbeschränkung in Bytes vorliegen und möchte diese dem Benutzer anzeigen. Aber nur die wenigsten Benutzer können mit der Größenangabe 1.048.576 Bytes etwas anfangen. Also möchte man die Größe dem Benutzer in ihm bekannten Größenangaben anzeigen - so wie man es von Windows kennt. Das .NET Framework selbst kennt meines Wissens eine solche Funktion nicht und daher ist es jedem Entwickler selbst überlassen, wie er diese Umrechnung vornimmt.

Auf meiner Suche nach einer Lösung für genau dieses Problem bin ich über einige Lösungsvorschläge bei stackoverflow gestolpert und einen davon fand ich extrem gut, so dass ich dem Problem und möglichen Lösungen diesen Blogpost widmen möchte.

1. Lösung: While-Loop

Dieser Ansatz wird gern genommen und liegt ja in der prozeduralen Denke auch auf der Hand: Man dividiert so lange durch 1024, bis eine Zahl übrig bleibt, die kleiner als 1024 ist. Hat man sich nun noch gemerkt, wie häufig man dies angewandt hat, so weiß man auch was man als Einheit dahinter schreiben muss.

var unit = new[] { "B", "KB", "MB", "GB", "TB", "PB" };
var index = 0;
var value = bytes;

while (value >= 1024)
{
 index++;
 value /= 1024;
}

var readable = string.Format("{0} {1}", value, unit[index]);

 

2. Lösung: Windows bemühen

Wie ich schon einleitend beschrieben habe, kann Windows diese Berechnung ja. Warum also nicht mit unmanaged Code auf die passende Windows-Funktion zugreifen? Die Shlwapi.dll stellt eine entsprechende Funktion bereit, mit der diese Formatierung vorgenommen werden kann.

[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern long StrFormatByteSize(
 long fileSize,
 [MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
 int bufferSize);


var sb = new StringBuilder(11);
StrFormatByteSize(bytes, sb, sb.Capacity);
var readable = sb.ToString();

OK, es ist eine Möglichkeit, aber keine die ich präferieren würde.

 

3. Lösung: Mathematisch

Und nun zu der Lösung, auf die ich zugegebenermaßen nicht gekommen bin, die ich aber sehr charmant finde. Mit Hilfe nur weniger mathematischer Funktionen lässt sich die Schleife aus der Lösung 1 ersetzen. Durch eine geschickte Kombination von Logarithmus-, Potenz- und Rundungsfunktionen lässt sich sowohl der gerundete Wert als auch die anzuzeigende Einheit ermitteln.

var unit = new[] { "B", "KB", "MB", "GB", "TB", "PB" };
var index = Convert.ToInt32(
    Math.Floor(Math.Log(Math.Abs(bytes), 1024)));
var value = Math.Round(bytes / Math.Pow(1024, index), 2);
var readable = string.Format("{0} {1}", value,  unit[index]);

Einziger Wert für den diese Variante nicht funktioniert ist 0 Byte.

Wie ganz häufig, so gibt es auch hier viele Wege, die zum Ziel führen und es bleibt jedem selbst überlassen, welchen Weg er wählt.

AllowHtml in ASP.NET MVC3

Meist ist es ja bei Eingabeformularen im Web gewünscht, dass eingegebene Werte beim späteren Rendern keinen Schaden anrichten. Genau dafür gibt es Request Validation. Dieses Feature sorgt dafür, dass keine Script-Tags eingegeben werden können, die später dann für Cross-Site-Scripting verwendet werden.
 
Nun gibt es aber doch auch Momente, in denen man doch möchte, dass HTML-Code in ein Textfeld eingegeben werden kann - z.B. dann wenn vom Benutzer Content für Webseiten generiert werden soll. Ein solches Anwendungsszenario ist häufig damit verbunden, dass ein Richtext-Editor zum Einsatz kommt, der dann eine normale Textarea mit entsprechenden Funktionen versieht. Typische Vertreter dafür sind TinyMCE oder auch der CKEditor. Wenn man die hier eingegebenen Daten dann versucht, an die Action zu übergeben, so wird das Ganze von der Request Validation bemerkt und eine entsprechende Fehlermeldung ausgegeben.
 

 
In der Fehlermeldung gibt es auch gleich einen Hinweis, was man ändern muss, damit man den Post trotzdem durchführen kann: Die Request-Validation für sämtliche Posts in der web.config zu deaktivieren. Diese Lösung ist nicht ganz so schlau.
 
Lösung 2, und bis MVC 2 auch die einzig andere Möglichkeit, ist es, die Request Validation für die komplette Action zu deaktivieren. Dazu setzt man einfach das ValidateInput-Attribut auf false.
 
[HttpPost, ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(Article article)
{
// do some database logic here
return View();
}
 
Ok, diese Lösung ist schon besser, da man eine bessere Kontrolle darüber hat, für welche Aktionen es gelten soll. Aber auch das ist noch nicht ganz optimal. Hier kommt nun das AllowHtml-Attribut von ASP.NET MVC 3 zur Geltung. Damit kann man auf einzelnen Properties des Models definieren, dass für diese die Request Validation nicht durchgeführt wird, da hier HTML-Code beinhaltet sein kann.
 
public class Article
{
 public int Id { get; set; }

 [AllowHtml]
 public string Content { get; set; }
}
 
Man muss nun nicht mehr sämtliche Properties von der Request Validation ausschließen, sondern kann hier dediziert definieren, wo HTML-Code enthalten sein darf und wo nicht.

Einladung zum Treffen der .NET Usergroup Dresden am 27.04.2011

Das kommende Treffen der .NET Usergroup Dresden findet am 27.04. um 18:00 Uhr statt. Wir treffen uns dieses Mal wieder in den Räumen der Communardo Software GmbH.
 
Thematisch widmen wir uns dieses Mal wieder den Web-Technologien etwas mehr. Diskussionsgrundlage und Beispiel soll die Anwendung BizzBingo sein. Diese verwendet unter anderem die folgenden Technologien und Frameworks:
  • ASP.NET MVC 3 mit Razor View Engine
  • Team City
  • Windows Azure
  • CQRS Architektur
  • BDD Unit Tests
  • Azure Deployment via PowerShell
  • jQuery
  • MSBuild
  • NuGet
Robert wird das Projekt vorstellen, das ursprünglich beim WebCamp im Juni 2010 in München seinen Anfang fand. Er wird auf die Technologie eingehen und auch den ein oder anderen Stolperstein. Ich denke bei dieser Themenvielfalt wird es sicher auch im Anschluss noch Bedarf und Gelegenheit für eine offene Diskussion geben.