Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Online File Management System mit ASP.NET und C# - Teil 1

Geschrieben von: Rudolf Ball
Kategorie: ASP.NET

This printed page brought to you by AlphaSierraPapa

Websites werden im Laufe der Zeit verändert, und wenn man nicht gerade ein CMS (Content Management System) einsetzt, dann bedeutet das in den meisten Fällen daß Änderungen durch Dateiupload mit FTP auf die Website kommen. Es gibt aber noch eine andere, komfortablere Möglichkeit, seine Daten, Dateien und Verzeichnisse online zu verwalten: ein Online File Management System (im weiteren nur mehr OFMS).

In dieser Artikelserie möchte ich Ihnen zeigen, wie man ein solches System mit ASP.NET und C# entwickelt. Es soll uns folgende Aktionen online zur Verfügung stellen:

Damit kann unser OFMS einiges mehr als ein normaler FTP Client. Und weitere Vorteile gewinnen wir ebenfalls: auf Reisen haben wir nicht immer einen FTP Client zur Hand, zB im Internetcafé, aber OFMS ist immer für uns da.

Verzeichnisse browsen

Um unsere Verzeichnisstruktur mit all den Dateien online ansehen zu können, müssen wir uns ein Bild davon machen, was wir eigentlich erzeugen wollen. Der Screenshot unten stellt dar, wie unser OFMS am Ende dieses Artikels aussehen soll.

Aus diesem Screenshot kann man leicht die Struktur des OFMS entnehmen. Ganz oben befindet sich unsere Toolbox, welche uns die Möglichkeit bieten soll, Dateien upzuloaden und neue Dateien bei Bedarf zu erzeugen. Gleich darunter befindet sich der Label Physischer_und_Logischer_Pfad, welcher den aktuellen physischen Pfad auf der Serverfestplatte und den logischen Pfad in unserer Website anzeigt. In unserem Fall handelt es sich physisch um "C:\inetpub\wwwroot\OFMS" und logisch um "/OFMS".

Direkt darunter (und noch nicht sichtbar) versteckt sich der Label StatusMessage, welcher bei Bedarf eine Fehlermeldung anzeigt. Als letzter und größter Block schlägt die Auflistung der Verzeichnisse und Dateien zu Buche. Diese ist dreigeteilt. Erstens benötigen wir einen "eine Verzeichnisebene höher"-Link, welcher uns von einem Unterverzeichnis in das übergeordnete Verzeichnis bringt. Zweitens listen wir Verzeichnisse auf. Bei den Verzeichnissen geben wir auch die Größe des Verzeichnisses an, welche der Größe der Dateien sowie der Unterverzeichnisse entspricht. Bei den Dateien geben wir weiters das Erstellungsdatum sowie das Datum der letzten Änderung an. Dies ist die Struktur, die wir im Folgenden darstellen wollen.

Unsere OFMS.aspx Seite beinhaltet zunächst nur die oben genannten WebControls. Diese wären ein Label für den logischen und physischen Pfad, ein Label für eventuelle Meldungen und ein leerer Table. Diese Tabelle wird Zeile für Zeile mit den Namen der Unterverzeichniss und den Namen der Dateien, welche unser aktuelles Verzeichnis enthält, gefüllt.

<asp:table id="DateienUndVerzeichnisse" Runat="server" Width="800px">
   <asp:TableRow>
      <asp:TableCell Width="40px"></asp:TableCell>
      <asp:TableCell Width="240px" Text="Index"></asp:TableCell>
      <asp:TableCell Width="40px"></asp:TableCell>
      <asp:TableCell Width="40px"></asp:TableCell>
      <asp:TableCell Width="40px"></asp:TableCell>
      <asp:TableCell Width="100px" HorizontalAlign="Center" Text="Grösse"></asp:TableCell>
      <asp:TableCell Width="150px" HorizontalAlign="Center" Text="erstellt am"></asp:TableCell>
      <asp:TableCell Width="150px" HorizontalAlign="Center" Text="geändert am"></asp:TableCell>
   </asp:TableRow>
</asp:table>

Bei jedem PageLoad-Event werden wir diesen Table dynamisch und Zeile für Zeile füllen. Unser PageLoad-Event sieht wie folgt aus:

if (!Page.IsPostBack)
{
   VerzeichnisPfad = Request.Params["Verzeichnis"];
   if (VerzeichnisPfad == null || VerzeichnisPfad == "/")
   {
      VerzeichnisPfad = Request.ApplicationPath.ToString();
   }
   else if (VerzeichnisPfad.EndsWith("/"))
   {
      VerzeichnisPfad = VerzeichnisPfad.Substring(0,VerzeichnisPfad.Length - 1);
   }

   Physicher_und_Logischer_Pfad.Text = "Virtueller Pfad: " + VerzeichnisPfad +
   "<br>Physischer Pfad: " + Server.MapPath(VerzeichnisPfad);
   
   TabelleDateienUndVerzeichnisseFuellen();
}

Sollte es sich um kein Postback-Event handeln, weisen wir der Variable VerzeichnisPfad einen Wert aus dem Querystring zu. Es wird geprüft, ob der VerzeichnisPfad leer oder ein Slash ("/") ist, was dem Rootverzeichnis entsprechen würde. In diesem Fall weisen wir der Variable VerzeichnisPfad das Ergebnis aus Request.ApplicationPath zu, welches dem logischen Rootverzeichnis unseres Webs entspricht ("/OFMS"). Sollte andererseits VerzeichnisPfad mit einem Slash enden, und somit kein Rootverzeichnis, aber ein Unterverzeichnis sein, so wird der Variable der finale Slash abgeschnitten. In weiterer Folge weisen wir dem Label Physicher_und_Logischer_Pfad als logischen Teil die Variable VerzeichnisPfad zu, physisch ermitteln wir die Position durch den Rückgabewert aus Server.MapPath.

Zu guter letzt rufen wir die umfangreiche Funktion TabelleDateienUndVerzeichnisseFuellen auf, welche den Table DateienUndVerzeichnisse füllt. Diese Funktion wird im Folgenden erläutert.

private void TabelleDateienUndVerzeichnisseFuellen()
{
   string Ort;
   DirectoryInfo ueberVerzeichnis;
   DirectoryInfo[] unterVerzeichnisse;
   FileInfo[] unterDateien;
   TableCell ZelleIcon;
   TableCell ZelleGroesse;
   TableCell ZelleErzeugt;
   TableCell ZelleVeraendert;
   System.Web.UI.WebControls.Image DateiIcon;

Im ersten Teil definieren wir die Variable Ort, welche den aktuellen Pfad halten wird. Die DirectoryInfos ueberVerzeichnis und unterVerzeichnis werden unser Superverzeichnis und eine Menge von Subverzeichnissen beinhalten. Das FileInfo unterDateien wird die Menge aller Dateien eines Verzeichnisses beinhalten. Die vier Zellen ZelleIcon, ZelleGroesse, ZelleErzeugt und ZelleVeraendert werden eine Zelle einer bestimmten Zeile unserer Tabelle DateienUndVerzeichnisse entsprechen. Das Bild DateiIcon wird für die verschiedenen Datei Icons benötigt.

try
{
   ueberVerzeichnis = new DirectoryInfo(Server.MapPath(VerzeichnisPfad));
   unterVerzeichnisse = ueberVerzeichnis.GetDirectories();
   unterDateien = ueberVerzeichnis.GetFiles();
}
catch (Exception ex)
{
   StatusMessage.Text = ex.Message;
   StatusMessage.Visible = true;
   return;
}

Wir versuchen nun, Verzeichnisse und Dateien einzulesen. Aus diesem Grund nutzen wir die Klasse DirectoryInfo. Die Variable ueberVerzeichnis repräsentiert das Verzeichnis, in welchem wir uns gerade befinden. Wir lesen es als "new DirectoryInfo" ein, als Eingabeparameter gilt die Variable VerzeichnisPfad. Die Arrays unterVerzeichnisse und unterDateien werden mit der Funktion GetDirectories oder GetFiles bestimmt. Im Falle eines Fehlers fangen wir ihn ab und schreiben ihn in den vormals unsichtbaren Label StatusMessage, welcher nun sichtbar wird.

TableRow Zeile;
TableCell ZelleLink;
HyperLink Link;

Nun benötigen wir drei Variablen. Zeile definiert eine Tabellenzeile, ZelleLink eine Zelle für einen Hyperlink und Link den Hyperlink.

Style StyleVerzeichnisse = new Style();
StyleVerzeichnisse.BackColor = Color.Gainsboro;
StyleVerzeichnisse.Font.Name = "Verdana";
StyleVerzeichnisse.Font.Bold = true;
StyleVerzeichnisse.Font.Underline = false;
StyleVerzeichnisse.Font.Size = 8;
StyleVerzeichnisse.ForeColor = Color.Black;
...

Wir definieren auch die Styles für die einzelnen Zellen. In diesem exemplarischen Codeausschnitt erzeugen wir einen neuen Style (new Style) und weisen ihm eine Reihe von Werten zu, zB Schriftart und Größe. Im Sourcecode werden Sie sehen, daß es auch eine Definition für Dateien (StyleDateien) und HyperLinks (StyleLink) gibt. Somit haben wir den allgemeinen Definitionsteil hinter uns gelassen und können damit beginnen, die Zeilen der Tabelle nach und nach mit Werten zu füllen. Als erstes beschäftigen wir uns mit der Tabellenzeile, die uns eine Verzeichnisebene nach oben bringt.

if (VerzeichnisPfad != "/")
{
   Zeile = new TableRow();
   ZelleLink = new TableCell();
   Link = new HyperLink();
   ZelleIcon = new TableCell();
   ZelleGroesse = new TableCell();
   DateiIcon = new System.Web.UI.WebControls.Image();

Sollte der VerzeichnisPfad ungleich dem Wurzelverzeichnis ("/") sein, erzeugen wir ein neues Objekt einer Zeile, einer LinkZelle, eines Hyperlinks usw.

DateiIcon.ImageUrl = "./Bilder/Ebene_hoeher.gif";
ZelleIcon.Controls.Add(DateiIcon);
ZelleIcon.HorizontalAlign = HorizontalAlign.Center;

Wir weisen dem Image DateiIcon das Symbol

zu. Mittels Contols.Add fügen wir der Zelle ZelleIcon das Image DateiIcon hinzu. Nach diesem System werden wir auch die anderen Zellen füllen. Als zusätzliches Attribut weisen wir der Zelle eine horizontale Ausrichtung zu.

Link.Text = "...";
int letzterSlash = VerzeichnisPfad.LastIndexOf("/");
Ort = VerzeichnisPfad.Substring(0,letzterSlash);

if (Ort.Length == 0)
{
   Ort = "/";
}

Link.NavigateUrl = "OFMS.aspx?Verzeichnis=" + Ort;
Link.ApplyStyle(StyleLink);
ZelleLink.Controls.Add(Link);

Dem Link geben wir als Text drei Punkte. Der Variable letzterSlash weisen wir den Indexwert des letzten Slashs in unserem Verzeichnispfad zu. Diese Information benötigen wir zum Abschneiden. Anschließend schneiden wir den Verzeichnispfad bis zum letzten Slash ab. Unser Ort ist nun das übergeordnete Verzeichnis. Nun prüfen wir, ob der Ort das Wurzelverzeichnis ist, um ihm gegebenenfalls den Wert "/" zuzuweisen. Wir erzeugen eine NavigateUrl für unseren Hyperlink, welcher wir dem Attribut Verzeichnis die Variable Ort zuweisen, unser Zielverzeichnis. Auch weisen wir dem Hyperlink den hartcodierten Style StyleLink zu und fügen es der Zelle hinzu.

ZelleGroesse = new TableCell();
ZelleGroesse.Text = "";
ZelleErzeugt = new TableCell();
ZelleErzeugt.Text = "";
ZelleVeraendert = new TableCell();
ZelleVeraendert.Text = "";

Da wir für diese Zeile - sie linkt uns ja eine Verzeichnisebene nach oben - weder eine Größe noch ein Erzeugungs- oder Veränderungsdatum besitzen erzeugen wir drei Dummyzellen, welche nur dazu dienen, keine Löcher in unserer Tabelle entstehen zu lassen.

Zeile.Cells.Add(ZelleIcon);
Zeile.Cells.Add(ZelleLink);
Zeile.Cells.Add(ZelleGroesse);
Zeile.Cells.Add(ZelleErzeugt);
Zeile.Cells.Add(ZelleVeraendert);
...
Zeile.ApplyStyle(StyleVerzeichnisse);
DateienUndVerzeichnisse.Rows.Add(Zeile);
}

Im letzten Schritt unserer Startzeile fügen wir die Zelle mit dem Icon und die Zelle mit dem Link sowie die Dummyzellen der Zeile hinzu, binden den Style StyleVerzeichnisse an die Zeile und fügen die Zeile der Tabelle hinzu. Somit haben wir die erste Zeile eingefügt.

foreach(DirectoryInfo unterVerzeichnis in unterVerzeichnisse)
{
   Zeile = new TableRow();
   ZelleLink = new TableCell();
   Link = new HyperLink();
   ...

Als zweite Zeilenart in unserer Tabelle werden wir die Verzeichnisse auflisten. Da dieser Teil des Codes sehr dem der "eine Verzeichnisebene höher" Zeile ähnelt, werden wir uns nur auf Veränderungen im Code beziehen. Der beigelegte, vollständige Sourcecode ist sehr ausführlich dokumentiert und somit auch leicht verständlich.

Link.Text = unterVerzeichnis.Name;
Ort = VerzeichnisPfad;

if (Ort.EndsWith("/"))
{
   Ort += unterVerzeichnis.Name;
}
else
{
   Ort += "/" + unterVerzeichnis.Name;
}

Ein Klick auf den Namen des Unterverzeichnisses soll dieses für uns öffnen. Daher sieht der Code für die Bestimmung der Variable Ort ein wenig anders aus. Ort wird anfänglich mit dem VerzeichnisPfad belegt. Sollte dieser keinen finalen Slash besitzen, wird er hinzugefügt. Ansonsten wird zum VerzeichnisPfad noch der Name des Unterverzeichnisses angefügt. Somit wird als Ort der vollständige Pfadname des Unterverzeichnisses dem Querystring übergeben.

ZelleGroesse.Text =
     DateiGroesseFormat(VerzeichnisGroesse(unterVerzeichnis.FullName));

Um die Größe des Unterverzeichnisses zu ermitteln, bedarf es ein wenig Programmieraufwand. Wie wir hier sehen, rufen wir zwei Subfunktionen auf. DateiGroesseFormat formatiert einen Größenwert in Bytes, Kilobytes und Megabytes. VerzeichnisGroesse ermittelt den Größenwert aus dem Unterverzeichnis. Diese rekursive Funktion folgt im Anschluß an den Code zum Füllen der Tabelle. Auch bei den Dateien ist der Code recht einsichtig, als einzige Neuerung füllen wir die Zellen "Datum der Erstellung" und "letzte Veränderung".

ZelleErzeugt.Text = String.Format("{0:MM/dd/yy hh:mm}", unterDatei.CreationTime);
ZelleVeraendert.Text = String.Format("{0:MM/dd/yy hh:mm}", unterDatei.LastWriteTime);

Die Eigenschaft CreationTime und LastWriteTime der jeweiligen Datei liefern das gewünschte Ergebnis. Zur Formatierung der Zeitangabe verwenden wir String.Format, als Formatierungsstring "dd.MM.yyyy hh:mm", was beispielsweise den Output "01.09.2002 12:33" erzeugen würde. Zu guter Letzt möchte ich noch die Funktion VerzeichnisGroesse unter die Lupe nehmen.

protected long VerzeichnisGroesse(string Pfad)
{
   long Groesse = 0;
   DirectoryInfo VerzeichnisInfo = new DirectoryInfo(Pfad);
   
foreach(FileInfo Datei in VerzeichnisInfo.GetFiles())
{
   Groesse += Datei.Length;
}

foreach (DirectoryInfo Unterverzeichnis in VerzeichnisInfo.GetDirectories())
{
   Groesse += VerzeichnisGroesse(Unterverzeichnis.FullName);
}

return Groesse;
}

Als Eingabeparameter nehmen wir den Pfad des Verzeichnisses, welches wir "wiegen" wollen. Neben einer Variable Groesse erzeugen wir eine DirectoryInfo auf das Verzeichnis. Da nun die Gesamtgröße des Verzeichnisses die Summe aller Dateigrößen und Verzeichnisgrößen ist, benötigen wir einen rekursiven Aufruf. Die Dateigrößen werden einfach addiert. Bei den Verzeichnisgrößen entspricht die Größe eines Verzeichnisses wieder dem Prozeduraufruf mit entsprechender Pfadangabe. Somit laufen wir den Verzeichnisbaum hinab, bis wir keine Unterverzeichnisse mehr finden können. Diese Summe aller Summen retournieren wir dann.

Schlußbemerkung

Im nächsten Artikel werden wir uns mit Bestandsveränderungen - Upload und Download - sowie dem Online-Editieren von Textdateien beschäftigen. Damit aus dem Browser ein richtiges Online-Administrationstool wird.

This printed page brought to you by AlphaSierraPapa

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20021031.zip

Verwandte Artikel

Directory Browsing a la .NET
http:/www.aspheute.com/artikel/20000804.htm
Ein Touch-Utility in C#
http:/www.aspheute.com/artikel/20020226.htm
Exception Handling in C#
http:/www.aspheute.com/artikel/20000724.htm
Online File Management System mit ASP.NET und C# - Teil 2
http:/www.aspheute.com/artikel/20021105.htm

 

©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.