Thomas Bandt

Über mich | Kontakt | Archiv

C# - Dateien in Mehrbenutzer-Umgebungen streamen

Mit ASP.NET respektive C# eine Datei zum Client zu streamen, ist kein großes Problem. Das macht man zum Beispiel, wenn man die Datei nicht direkt zum Download anbieten möchte, weil man sie vielleicht nur für eingeloggte Benutzer bereitstellen oder die Anzahl der Downloads mitloggen möchte.

Problematisch kann es allerdings immer dann werden, wenn mehrere Benutzer gleichzeitig versuchen, die Datei herunterzuladen, vielleicht sogar in verschiedenen von einander unabhängigen Anwendungen. Die Folge ist eine IOException mit dem schönen Text:

Der Prozess kann nicht auf die Datei D:\xyz.pdf zugreifen, da sie von einem anderen Prozess verwendet wird.

Ein Lösungsansatz, der sich anbietet, ist es diesen Ausnahmefall abzufangen, und das Ganze so lange zu probieren, bis die Datei wieder freigegeben ist. Damit das Ganze nicht in einer Endlosschleife endet, habe ich die Anzahl der Versuche allerdings auf 10 beschränkt:

context.Response.ContentType = contenttype;
context.Response.AddHeader("content-disposition", "attachment; filename=" + filename);

byte[] getContent = null;
bool loadFile = true;

if (!File.Exists(filepath))
    throw new FileNotFoundException("Die Datei " + filepath + " existiert nicht.");

int exceptionCount = 0;

while (loadFile)
{
    try
    {
        using (FileStream sourceFile = new FileStream(filepath, FileMode.Open))
        {
            long FileSize;
            FileSize = sourceFile.Length;
            getContent = new byte[(int) FileSize];
            sourceFile.Read(getContent, 0, (int) sourceFile.Length);
            loadFile = false;
        }
    }
    catch(IOException exc)
    {
        if(exceptionCount < 10)
        {
            exceptionCount++;
            System.Threading.Thread.Sleep(1000);
        }
        else
        {
            throw new IOException("Der Anhang kann  auch nach 10 Versuchen nicht geöffnet werden", exc);
        }
    }
}

context.Response.BinaryWrite(getContent);
context.Response.End();

That's it.

Kommentare

  1. rosch schrieb am Freitag, 4. Juli 2008 13:16:00 Uhr:

    Das öffnen der Datei mittels

    using (FileStream sourceFile = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read))

    sollte das parallele Lesen erlauben. Außerdem ist es keine gute Idee, die Datei in einem Rutsch in den Speicher zu lesen:

    getContent = new byte[(int) FileSize];
    sourceFile.Read(getContent, 0, (int) sourceFile.Length);

    Wesentlich besser wäre eine Schleife:

    public static bool Pipe(Stream inputStream, Stream outputStream)
    {

    const int LEN = 4096;
    byte[] buffer = new byte[LEN];

    int cnt;
    while ((cnt = inputStream.Read(buffer, 0, LEN)) != 0)
    {
    outputStream.Write(buffer, 0, cnt);
    }
    inputStream.Flush();
    outputStream.Flush();
    return true;
    }

    [ohne Exceptionhandling]

  2. Thomas schrieb am Freitag, 4. Juli 2008 13:36:00 Uhr:

    new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)

    -> Typischer Copy&Paste-Fehler, ein Versuch mit IntelliSense wär's wohl gewesen. Danke.

    "Wesentlich besser wäre eine Schleife: "

    Warum?

  3. rosch schrieb am Freitag, 4. Juli 2008 14:17:00 Uhr:

    Eine Schleife wäre besser, weil dann die Datei nur "häppchenweise" ausgelesen und an den Client gestreamt (gib es so ein Wort) wird - damit sinkt die Belastung für den Arbeitsspeicher. Bei kleinen Dateien ist das kein Problem. Aber eine mehrere MB große Datei die von mehreren Nutzern gleichzeitig (in den Speicher) geladen wird...

    (Den Beispielcode oben verwende ich in einer C/S-Anwendung, um Dateien vom Client auf den Server zu kopieren. Klappt sehr gut auch mit sehr großen Dateien.)
  4. Thomas schrieb am Freitag, 4. Juli 2008 14:36:00 Uhr:

    Macht Sinn, werde ich berücksichtigen. Danke! :-)
  5. Thomas goes .NET schrieb am Montag, 13. Juli 2009 13:04:00 Uhr:

    Ich hatte mich zum Thema vor einem Jahr bereits


« Zurück  |  Weiter »