OutOfMemory-Exception bei MemoryStream

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    OutOfMemory-Exception bei MemoryStream

    Irgendwie stehe ich gerade vor einem für mich unlösbaren Problem.

    Erstmal zu meinem Vorhaben: für meine HttpPostRequestLib möchte ich etwas optimieren, bzw. möglich machen große Dateien hochzuladen.
    Das Problem ist der Content muss erstmal zusammen gestellt werden bevor er in den HttpStream geschrieben werden kann, da ich ja die Content-Length benötige.
    In einer Schleife erstelle ich nun also die POST Variablen + die Dateien die hochgeladen werden sollen.

    Momentan schreibe ich alles in einen MemoryStream, da vorher bei List(Of Byte) oder einem ByteArray ständig OutOfMemory-Exceptions aufgetreten sind und ich nahm an dass diese Datentypen einfach nicht die notwendige Größe haben.
    Nun tritt jedoch auch bei dem MemoryStream die OutOfMemory-Exception auf, wenn ich mir die aktuelle Länge des MemoryStreams anschaue sind das 262259612 Bytes, aber es muss doch möglich sein soviel in den Zwischenspeicher zu laden oder wird da durch VB irgendwie die Größe beschränkt? Bzw. jemand eine Idee wie ich es anders lösen kann?
    Hi
    Also ich habe kein Problem, ein 1024^3-elementiges Array zu erstellen. Ein Memorystream muss aber beim schreiben auch den Platz allokieren, der notwendig ist. Damit ist da das gleiche Problem, wie bei Arrays zu erwarten. Müssen die Daten entsprechend User-/bzw. Programmeingaben zeitlich versetzt ausgewertet werden oder stehen die Daten zum Zeitpunkt des Sendens bereits fest? Wie groß waren die geladenen Daten bis jetzt und wie viel RAM hast du? Ansonsten könntest du die Datei in mehreren Packeten verschicken.

    Gruß
    ~blaze~
    naja - das berührt das Grundkonzept "Stream".
    Ein Stream ist ein Dings, wo man was reinschreibt, und der Stream reicht die Daten weiter an einen BackBuffer, oder Datensenke, oder wiemans nennen will.
    Ein FileStream zB. schreibt die Daten in eine Datei weg. Ein NetworkStream schreibt die Daten über TCP weg.

    MemoryStream hat halt als BackBuffer nur ein zusammenhängendes Byte-Array im Arbeitsspeicher (wasser nach Erfordernis vergrößert) - und da ist halt irgendwann Sense, wenn kein genügend großes array mehr alloziiert wern kann.
    Ich muss die Daten zuerst vorbereiten, also Dateien und die dazugehörigen Informationen. Diese muss ich zunächst alles zwischenspeichern, damit ich die absolute größe habe um sie im Header angeben zu können und der Header wird ja VOR dem Content gesendet, deswegen muss ich den gesamten Content erstmal vorbereiten und irgendwo abspeichern.
    Wie meinst du das? Stream Länge kann ich ja noch nicht auslesen, wenn die Datei noch nicht in dem MemoryStream drin ist.

    Also wie folgt muss der Content aussehen

    Quellcode

    1. --Boundary
    2. Content-Name: filename="datei.avi": mimetype: video/avi
    3. Content-Type: binary
    4. [hier die Datei als Byte]
    5. --Boundary
    6. Content-Name: filename="test.txt": mimetype: text/plain
    7. Content-Type: binary
    8. [hier die Datei als Byte]


    Dieser Content wird in einer Schleife erstellt ausgelesen aus einer Liste wo die Dateipfade drin stehen.
    Nachdem dieser Content erstellt wurde kann ich ja nun die Länge ermitteln, diese dem Header zuweisen.
    DANACH nutze ich diesen Content und schreibe ihn Byteweise in Blöcken in den Stream des Requests.
    Und beim erstellen des Content kommt bei mir der Fehler, wenn ich versuche die Datei an die entsprechende Stelle einzulesen.
    So ganz klar ist mir das jetzt noch nicht. Du hast ja sFile.Data geschrieben (damit andere Leser den Zusammenhang verstehen: Das ergibt sich aus dem alten Problem im anderen Thread). Welchen Ursprung ist denn diese Information? Welchen Typs sind die Werte? Du versuchst im Moment schon, die Daten lokal einzulesen und über den Http-Request zu verschicken oder? Demnach müsstest du ja die Datei per Stream auslesen können. Außerdem muss die Länge in jedem Fall bekannt sein, da sonst der Empfänger nichts mit den Informationen anfangen kann oder wird der Stream dann geschlossen?

    Gruß
    ~blaze~
    sFile ist eine eigene Klasse in der Informationen zu der Datei stehen, z.B. der Variablenname, Dateiname, Mimetype und die Daten als Byte.

    Ja ich lese die Datei lokal ein und möchte sie Byteweise via http Request verschicken. Per Stream muss ich die Datei nicht auslesen, ich lese die Datei mittel IO.ReadAllBytes() ein und füge sie später in der Schleife dem Content hinzu.

    Die Länge der Datei ist in der Tat bekannt aber die anderen Daten eben nicht, also diese ganzen Informationen zu der Datei. Somit müsste ich die Datei Informationen ja 2 mal erstellen, einmal damit ich die Länge habe und danach nochmal um es in den Stream zu schreiben.

    Direkt in den Stream schreiben kann ich ja nicht, weil ich eben zuerst im Header die Content-Length angeben muss.

    VB.NET-Quellcode

    1. Dim l As List(Of Byte)
    2. For listevondateien
    3. l.AddRange(GetBytes("--Boundary"))
    4. l.AddRange(GetBytes("--Content-Name: filename='datei.avi': mime-type: video/avi"))
    5. l.AddRange(GetBytes("--Content-Type: binary"))
    6. l.AddRange(ReadAllBytes("c:\datei.avi"))
    7. Next
    8. Dim b As Byte() = l.ToArray()
    9. myRequest.ContentLength = b.length
    10. For i As Integer = 0 To b.Length - 1 Step 4095
    11. stream.Write(b, i, 4095)
    12. Next i


    So mal ein schematisches Beispiel wie ich es mache. Also ich muss zuerst den Content haben um die Länge anzugeben und danach wird dieses Array von Byte in den Stream geschrieben.

    Aber ich glaube ich habe bereits eine Lösung für mein Dillemma, werde es morgen mal versuchen umzusetzten. Trotzdem danke für die bemühungen =)
    Nur so: Ich hätte es mit

    VB.NET-Quellcode

    1. Dim bw As New IO.BinaryWriter(stream)
    2. Dim fileStream As IO.FileStream = New IO.FileStream("file", IO.FileMode.Open)
    3. Dim buffer(4095) As Byte
    4. Dim count As Integer
    5. bw.Write(encoding.GetBytes("--Boundary"))
    6. bw.Write(encoding.GetBytes(""--Content-Name: filename='datei.avi': mime-type: video/avi"))
    7. bw.Write(encoding.GetBytes("--Content-Type: binary"))
    8. 'usw.
    9. 'Laenge schreiben
    10. bw.Write(fileStream.Length)
    11. 'Dateicontent schreiben
    12. While count = buffer.Length
    13. fileStream.Read(buffer, 0, count)
    14. bw.Write(buffer)
    15. End While

    Damit wären die Daten gepuffert und die Länge vor dem einlesen vorhanden und bekannt.

    Gruß
    ~blaze~

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()

    Damit wäre die länge EINER Datei bekannt. Zudem muss ich ja nicht nur die Länge der Datei sondern auch zusammen mit den Strings, weil die sind ja auch mit Content sind. Wären es nur die Dateien Längen wäre das Problem ja viel einfacherer, problematisch sind die Strings auch noch mit in die Länge zu bekommen.
    Du könntest ja den String davor schon mal konkatenieren und anschließend die Länge in Bytes durch Encoding.GetByteCount herausfinden. Dann hast du die Gesamtlänge. Wenn du die Dateien bereits vorher öffnest und in ein Array von Streams zwischenspeicherst, kannst du die Länge bereits vorher ermitteln. Oder du machst es über FileInfo (was allerdings nicht so vorteilhaft ist, da die Streams ja sowieso geöffnet werden).

    Ich besser oben noch einen kleinen Fehler aus.

    Gruß
    ~blaze~