it-swarm.com.de

Wie kopiere ich den Inhalt eines Streams in einen anderen?

Was ist der beste Weg, um den Inhalt eines Streams in einen anderen zu kopieren? Gibt es dafür eine Standard-Utility-Methode?

504
Anton

Ab .NET 4.5 gibt es das Stream.CopyToAsync Methode

input.CopyToAsync(output);

Dies gibt ein Task zurück, das fortgesetzt werden kann, wenn es abgeschlossen ist, wie folgt:

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.

Beachten Sie, dass je nachdem, wo der Aufruf von CopyToAsync erfolgt, der folgende Code möglicherweise im selben Thread fortgesetzt wird, der ihn aufgerufen hat, oder auch nicht.

Das SynchronizationContext , das beim Aufruf von await erfasst wurde, bestimmt, auf welchem ​​Thread die Fortsetzung ausgeführt wird.

Darüber hinaus werden bei diesem Aufruf (und dies ist ein zu änderndes Implementierungsdetail) weiterhin Lese- und Schreibvorgänge ausgeführt (es wird lediglich kein Thread verschwendet, der beim Abschluss der E/A blockiert).

Ab .NET 4.0 gibt es das Stream.CopyTo Methode

input.CopyTo(output);

Für .NET 3.5 und früher

Es ist nichts in das Framework eingebettet, was dabei helfen könnte. Sie müssen den Inhalt manuell kopieren, wie folgt:

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    int read;
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write (buffer, 0, read);
    }
}

Hinweis 1: Mit dieser Methode können Sie den Fortschritt melden (x bisher gelesene Bytes ...)
Hinweis 2: Warum eine feste Puffergröße verwenden und nicht input.Length? Weil diese Länge möglicherweise nicht verfügbar ist! Aus dem docs :

Wenn eine von Stream abgeleitete Klasse das Suchen nicht unterstützt, lösen Aufrufe von Length, SetLength, Position und Seek eine NotSupportedException aus.

678
Nick

MemoryStream hat .WriteTo (Outstream);

und .NET 4.0 hat .CopyTo für ein normales Stream-Objekt.

.NET 4.0:

instream.CopyTo(outstream);
64
Joshua

Ich verwende die folgenden Erweiterungsmethoden. Sie haben optimierte Überladungen für den Fall, dass ein Stream ein MemoryStream ist.

    public static void CopyTo(this Stream src, Stream dest)
    {
        int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
        byte[] buffer = new byte[size];
        int n;
        do
        {
            n = src.Read(buffer, 0, buffer.Length);
            dest.Write(buffer, 0, n);
        } while (n != 0);           
    }

    public static void CopyTo(this MemoryStream src, Stream dest)
    {
        dest.Write(src.GetBuffer(), (int)src.Position, (int)(src.Length - src.Position));
    }

    public static void CopyTo(this Stream src, MemoryStream dest)
    {
        if (src.CanSeek)
        {
            int pos = (int)dest.Position;
            int length = (int)(src.Length - src.Position) + pos;
            dest.SetLength(length); 

            while(pos < length)                
                pos += src.Read(dest.GetBuffer(), pos, length - pos);
        }
        else
            src.CopyTo((Stream)dest);
    }
31
Eloff

Es gibt tatsächlich eine weniger umständliche Art, eine Stream-Kopie zu erstellen. Beachten Sie jedoch, dass dies bedeutet, dass Sie die gesamte Datei im Speicher speichern können. Versuchen Sie nicht, dies zu verwenden, wenn Sie mit Dateien arbeiten, die mehrere hundert Megabyte oder mehr groß sind, ohne Vorsicht.

public static void CopyStream(Stream input, Stream output)
{
  using (StreamReader reader = new StreamReader(input))
  using (StreamWriter writer = new StreamWriter(output))
  {
    writer.Write(reader.ReadToEnd());
  }
}

HINWEIS: Es können auch Probleme mit Binärdaten und Zeichenkodierungen auftreten.

2
hannasm

.NET Framework 4 führt die neue "CopyTo" -Methode der Stream-Klasse des System.IO-Namespace ein. Mit dieser Methode können wir einen Stream in einen anderen Stream einer anderen Stream-Klasse kopieren.

Hier ist ein Beispiel dafür.

    FileStream objFileStream = File.Open(Server.MapPath("TextFile.txt"), FileMode.Open);
    Response.Write(string.Format("FileStream Content length: {0}", objFileStream.Length.ToString()));

    MemoryStream objMemoryStream = new MemoryStream();

    // Copy File Stream to Memory Stream using CopyTo method
    objFileStream.CopyTo(objMemoryStream);
    Response.Write("<br/><br/>");
    Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString()));
    Response.Write("<br/><br/>");
1
Jayesh Sorathia

Die grundlegenden Fragen, die Implementierungen von "CopyStream" unterscheiden, sind:

  • größe des Lesepuffers
  • größe der Schreibvorgänge
  • Können wir mehr als einen Thread verwenden (Schreiben während wir lesen)?.

Die Antworten auf diese Fragen führen zu sehr unterschiedlichen Implementierungen von CopyStream und hängen davon ab, über welche Streams Sie verfügen und was Sie optimieren möchten. Die "beste" Implementierung müsste sogar wissen, auf welcher spezifischen Hardware die Streams lesen und schreiben.

1
fryguybob

Leider gibt es keine wirklich einfache Lösung. Sie können so etwas ausprobieren:

Stream s1, s2;
byte[] buffer = new byte[4096];
int bytesRead = 0;
while (bytesRead = s1.Read(buffer, 0, buffer.Length) > 0) s2.Write(buffer, 0, bytesRead);
s1.Close(); s2.Close();

Das Problem dabei ist jedoch, dass sich die unterschiedliche Implementierung der Stream-Klasse möglicherweise anders verhält, wenn nichts zu lesen ist. Ein Stream, der eine Datei von einer lokalen Festplatte liest, wird wahrscheinlich blockiert, bis der Lesevorgang genügend Daten von der Festplatte gelesen hat, um den Puffer zu füllen, und nur dann weniger Daten zurückgibt, wenn das Dateiende erreicht ist. Andererseits gibt ein Stream, der aus dem Netzwerk gelesen wird, möglicherweise weniger Daten zurück, obwohl noch mehr Daten zu empfangen sind.

Überprüfen Sie immer die Dokumentation der spezifischen Stream-Klasse, die Sie verwenden, bevor Sie eine generische Lösung verwenden.

0
Tamas Czinege

wenn Sie möchten, dass eine Prozedur einen Stream auf einen anderen kopiert, der von Nick gepostet wurde, aber die Position nicht zurückgesetzt wurde, sollte dies der Fall sein

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    long TempPos = input.Position;
    while (true)    
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
    input.Position = TempPos;// or you make Position = 0 to set it at the start
}

wenn es jedoch zur Laufzeit keine Prozedur verwendet, sollten Sie den Speicherdatenstrom verwenden

Stream output = new MemoryStream();
byte[] buffer = new byte[32768]; // or you specify the size you want of your buffer
long TempPos = input.Position;
while (true)    
{
    int read = input.Read (buffer, 0, buffer.Length);
    if (read <= 0)
        return;
    output.Write (buffer, 0, read);
 }
    input.Position = TempPos;// or you make Position = 0 to set it at the start
0
Kronass

Da keine der Antworten eine asynchrone Art des Kopierens von einem Stream in einen anderen behandelt hat, ist hier ein Muster, das ich in einer Portweiterleitungsanwendung erfolgreich verwendet habe, um Daten von einem Netzwerkstream in einen anderen zu kopieren. Es fehlt eine Ausnahmebehandlung, um das Muster hervorzuheben.

const int BUFFER_SIZE = 4096;

static byte[] bufferForRead = new byte[BUFFER_SIZE];
static byte[] bufferForWrite = new byte[BUFFER_SIZE];

static Stream sourceStream = new MemoryStream();
static Stream destinationStream = new MemoryStream();

static void Main(string[] args)
{
    // Initial read from source stream
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginReadCallback(IAsyncResult asyncRes)
{
    // Finish reading from source stream
    int bytesRead = sourceStream.EndRead(asyncRes);
    // Make a copy of the buffer as we'll start another read immediately
    Array.Copy(bufferForRead, 0, bufferForWrite, 0, bytesRead);
    // Write copied buffer to destination stream
    destinationStream.BeginWrite(bufferForWrite, 0, bytesRead, BeginWriteCallback, null);
    // Start the next read (looks like async recursion I guess)
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginWriteCallback(IAsyncResult asyncRes)
{
    // Finish writing to destination stream
    destinationStream.EndWrite(asyncRes);
}
0
mdonatas

Für .NET 3.5 und vor dem Versuch:

MemoryStream1.WriteTo(MemoryStream2);
0
ntiago

Einfach und sicher - erstellen Sie einen neuen Stream aus der Originalquelle:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
0
Graham Laight

Je nachdem, mit welcher Art von Stream Sie arbeiten, gibt es möglicherweise eine Möglichkeit, dies effizienter zu tun. Wenn Sie einen oder beide Streams in einen MemoryStream konvertieren können, können Sie mit der GetBuffer-Methode direkt mit einem Byte-Array arbeiten, das Ihre Daten darstellt. Auf diese Weise können Sie Methoden wie Array.CopyTo verwenden, mit denen alle von fryguybob aufgeworfenen Probleme beseitigt werden. Sie können einfach darauf vertrauen, dass .NET den optimalen Weg zum Kopieren der Daten kennt.

0
Coderer