Vielleicht noch wichtiger an dieser Stelle, wie kopiert man den Inhalt "streambar", was bedeutet, dass er nur den Quell-Stream kopiert, wenn etwas den Ziel-Stream verbraucht ...?
Dies gibt ein zurück Task, 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, CopyToAsyncdass der folgende Code je nachdem, wo der Aufruf erfolgt, möglicherweise im selben Thread fortgesetzt wird, in dem er aufgerufen wurde.
Der SynchronizationContextbeim Aufruf erfasste awaitThread bestimmt, auf welchem Thread die Fortsetzung ausgeführt wird.
Darüber hinaus werden bei diesem Aufruf (und dies ist ein Implementierungsdetail, das sich ändern kann) weiterhin Lese- und Schreibvorgänge ausgeführt (es wird nur kein Thread verschwendet, der beim Abschluss der E / A blockiert wird).
Hinweis 1: Mit dieser Methode können Sie über den Fortschritt berichten (bisher gelesene x Bytes ...).
Hinweis 2: Warum eine feste Puffergröße verwenden und nicht input.Length? Weil diese Länge möglicherweise nicht verfügbar ist! Aus den Dokumenten :
Wenn eine von Stream abgeleitete Klasse das Suchen nicht unterstützt, lösen Aufrufe von Length, SetLength, Position und Seek eine NotSupportedException aus.
Beachten Sie, dass dies nicht der schnellste Weg ist. Im bereitgestellten Code-Snippet müssen Sie warten, bis der Schreibvorgang abgeschlossen ist, bevor ein neuer Block gelesen wird. Beim asynchronen Lesen und Schreiben verschwindet dieses Warten. In einigen Situationen wird die Kopie dadurch doppelt so schnell. Dadurch wird der Code jedoch viel komplizierter. Wenn also Geschwindigkeit kein Problem ist, halten Sie ihn einfach und verwenden Sie diese einfache Schleife. Diese Frage auf StackOverflow hat einen Code, der das asynchrone Lesen / Schreiben veranschaulicht : stackoverflow.com/questions/1540658/… Grüße, Sebastiaan
Sebastiaan M
16
FWIW, bei meinen Tests habe ich festgestellt, dass 4096 tatsächlich schneller als 32 KB ist. Dies hängt damit zusammen, wie die CLR Chunks über eine bestimmte Größe zuweist. Aus diesem Grund verwendet die .NET-Implementierung von Stream.CopyTo anscheinend 4096.
Jeff
1
Wenn Sie wissen möchten, wie CopyToAsync implementiert ist, oder Änderungen wie ich vornehmen möchten (ich musste die maximale Anzahl der zu kopierenden Bytes angeben können), ist es als CopyStreamToStreamAsync in "Beispiele für die parallele Programmierung mit .NET Framework" code.msdn verfügbar .microsoft.com / ParExtSamples
Michael
1
FIY, optimale Puffergröße iz 81920Bytes, nicht32768
Ich sehe nicht viele Beispiele im Web, die diese Methoden verwenden. Liegt das daran, dass sie ziemlich neu sind oder gibt es einige Einschränkungen?
GeneS
3
Dies liegt daran, dass sie in .NET 4.0 neu sind. Stream.CopyTo () macht im Grunde genau das Gleiche für die Schleife wie die genehmigte Antwort, mit einigen zusätzlichen Überprüfungen der Integrität. Die Standardpuffergröße ist 4096, es gibt jedoch auch eine Überlastung, um eine größere anzugeben.
Michael Edenfield
9
Der Stream muss nach dem Kopieren zurückgespult werden: instream.Position = 0;
Draykos
6
Zusätzlich zum Zurückspulen des Eingabestreams musste der Ausgabestream zurückgespult werden: outstream.Position = 0;
JonH
32
Ich benutze die folgenden Erweiterungsmethoden. Sie haben Überladungen optimiert, wenn ein Stream ein MemoryStream ist.
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 des Lesens)?
Die Antworten auf diese Fragen führen zu sehr unterschiedlichen Implementierungen von CopyStream und hängen davon ab, welche Art von Streams Sie haben und was Sie optimieren möchten. Die "beste" Implementierung müsste sogar wissen, auf welche spezifische Hardware die Streams lesen und schreiben.
... oder die beste Implementierung könnte Überladungen aufweisen, damit Sie die Puffergröße, die Schreibgröße und die zulässigen Threads angeben können?
MarkJ
1
Es gibt tatsächlich eine weniger hartnäckige Möglichkeit, 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 ohne Vorsicht mit Dateien arbeiten, die Hunderte von Megabyte oder mehr umfassen.
publicstaticvoidCopyStream(Stream input,Stream output){
using (StreamReader reader =newStreamReader(input))
using (StreamWriter writer =newStreamWriter(output)){
writer.Write(reader.ReadToEnd());}}
HINWEIS: Möglicherweise gibt es auch Probleme mit Binärdaten und Zeichencodierungen.
Der Standardkonstruktor für StreamWriter erstellt einen UTF8-Stream ohne Stückliste ( msdn.microsoft.com/en-us/library/fysy0a4b.aspx ), sodass keine Gefahr von Codierungsproblemen besteht. Binärdaten sollten mit ziemlicher Sicherheit nicht auf diese Weise kopiert werden.
kͩeͣmͮpͥ 9
14
man könnte leicht argumentieren, dass das Laden "der gesamten Datei im Speicher" kaum als "weniger schwerfällig" angesehen wird.
Seph
Ich bekomme Outmemory Ausnahme aus diesem Grund
ColacX
Dies ist kein Stream zu Stream. reader.ReadToEnd()legt alles in RAM
Bizhan
1
.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.
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 die Leseoperation 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 kann ein Stream, der aus dem Netzwerk liest, weniger Daten zurückgeben, obwohl 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.
Die generische Lösung wird hier funktionieren - Nicks Antwort ist gut. Die Puffergröße ist natürlich eine willkürliche Wahl, aber 32 KB klingen vernünftig. Ich denke, Nicks Lösung ist richtig, die Streams nicht zu schließen - überlassen Sie das dem Besitzer.
Jon Skeet
0
Je nachdem, mit welcher Art von Stream Sie arbeiten, kann dies effizienter durchgeführt werden. Wenn Sie einen oder beide Ihrer Streams in einen MemoryStream konvertieren können, können Sie die GetBuffer-Methode verwenden, um direkt mit einem Byte-Array zu arbeiten, das Ihre Daten darstellt. Auf diese Weise können Sie Methoden wie Array.CopyTo verwenden, die alle von fryguybob aufgeworfenen Probleme abstrahieren. Sie können .NET einfach vertrauen, um die optimale Methode zum Kopieren der Daten zu kennen.
Wenn Sie möchten, dass ein Prozess einen Stream in einen anderen kopiert, der von Nick gepostet wurde, ist dies in Ordnung, aber es fehlt das Zurücksetzen der Position
publicstaticvoidCopyStream(Stream input,Stream output){byte[] buffer =newbyte[32768];longTempPos= 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 einen Speicherstrom verwenden
Stream output =newMemoryStream();byte[] buffer =newbyte[32768];// or you specify the size you want of your bufferlongTempPos= 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
Sie sollten die Position des Eingabestreams nicht ändern, da nicht alle Streams einen wahlfreien Zugriff ermöglichen. In einem Netzwerkstrom können Sie beispielsweise die Position nicht ändern, sondern nur lesen und / oder schreiben.
R. Martinho Fernandes
0
Da keine der Antworten eine asynchrone Methode zum Kopieren von einem Stream in einen anderen behandelt hat, ist hier ein Muster, das ich erfolgreich in einer Portweiterleitungsanwendung verwendet habe, um Daten von einem Netzwerkstrom in einen anderen zu kopieren. Es fehlt die Ausnahmebehandlung, um das Muster hervorzuheben.
constint BUFFER_SIZE =4096;staticbyte[] bufferForRead =newbyte[BUFFER_SIZE];staticbyte[] bufferForWrite =newbyte[BUFFER_SIZE];staticStream sourceStream =newMemoryStream();staticStream destinationStream =newMemoryStream();staticvoidMain(string[] args){// Initial read from source stream
sourceStream.BeginRead(bufferForRead,0, BUFFER_SIZE,BeginReadCallback,null);}privatestaticvoidBeginReadCallback(IAsyncResult asyncRes){// Finish reading from source streamint bytesRead = sourceStream.EndRead(asyncRes);// Make a copy of the buffer as we'll start another read immediatelyArray.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);}privatestaticvoidBeginWriteCallback(IAsyncResult asyncRes){// Finish writing to destination stream
destinationStream.EndWrite(asyncRes);}
Wenn der zweite Lesevorgang vor dem ersten Schreibvorgang abgeschlossen ist, werden Sie den Inhalt von bufferForWrite vom ersten Lesevorgang an überschreiben, bevor er ausgeschrieben wird.
Antworten:
Ab .NET 4.5 gibt es die
Stream.CopyToAsync
MethodeDies gibt ein zurück
Task
, das fortgesetzt werden kann, wenn es abgeschlossen ist, wie folgt :Beachten Sie,
CopyToAsync
dass der folgende Code je nachdem, wo der Aufruf erfolgt, möglicherweise im selben Thread fortgesetzt wird, in dem er aufgerufen wurde.Der
SynchronizationContext
beim Aufruf erfassteawait
Thread bestimmt, auf welchem Thread die Fortsetzung ausgeführt wird.Darüber hinaus werden bei diesem Aufruf (und dies ist ein Implementierungsdetail, das sich ändern kann) weiterhin Lese- und Schreibvorgänge ausgeführt (es wird nur kein Thread verschwendet, der beim Abschluss der E / A blockiert wird).
Ab .NET 4.0 gibt es die
Stream.CopyTo
MethodeFür .NET 3.5 und früher
Es ist nichts in das Framework eingebrannt, um dies zu unterstützen. Sie müssen den Inhalt manuell kopieren, wie folgt:
Hinweis 1: Mit dieser Methode können Sie über den Fortschritt berichten (bisher gelesene x Bytes ...).
Hinweis 2: Warum eine feste Puffergröße verwenden und nicht
input.Length
? Weil diese Länge möglicherweise nicht verfügbar ist! Aus den Dokumenten :quelle
81920
Bytes, nicht32768
MemoryStream hat .WriteTo (Outstream);
und .NET 4.0 hat .CopyTo für ein normales Stream-Objekt.
.NET 4.0:
quelle
Ich benutze die folgenden Erweiterungsmethoden. Sie haben Überladungen optimiert, wenn ein Stream ein MemoryStream ist.
quelle
Die grundlegenden Fragen, die Implementierungen von "CopyStream" unterscheiden, sind:
Die Antworten auf diese Fragen führen zu sehr unterschiedlichen Implementierungen von CopyStream und hängen davon ab, welche Art von Streams Sie haben und was Sie optimieren möchten. Die "beste" Implementierung müsste sogar wissen, auf welche spezifische Hardware die Streams lesen und schreiben.
quelle
Es gibt tatsächlich eine weniger hartnäckige Möglichkeit, 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 ohne Vorsicht mit Dateien arbeiten, die Hunderte von Megabyte oder mehr umfassen.
HINWEIS: Möglicherweise gibt es auch Probleme mit Binärdaten und Zeichencodierungen.
quelle
reader.ReadToEnd()
legt alles in RAM.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.
quelle
CopyToAsync()
wird empfohlen.Leider gibt es keine wirklich einfache Lösung. Sie können so etwas versuchen:
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 die Leseoperation 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 kann ein Stream, der aus dem Netzwerk liest, weniger Daten zurückgeben, obwohl 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.
quelle
Je nachdem, mit welcher Art von Stream Sie arbeiten, kann dies effizienter durchgeführt werden. Wenn Sie einen oder beide Ihrer Streams in einen MemoryStream konvertieren können, können Sie die GetBuffer-Methode verwenden, um direkt mit einem Byte-Array zu arbeiten, das Ihre Daten darstellt. Auf diese Weise können Sie Methoden wie Array.CopyTo verwenden, die alle von fryguybob aufgeworfenen Probleme abstrahieren. Sie können .NET einfach vertrauen, um die optimale Methode zum Kopieren der Daten zu kennen.
quelle
Wenn Sie möchten, dass ein Prozess einen Stream in einen anderen kopiert, der von Nick gepostet wurde, ist dies in Ordnung, aber es fehlt das Zurücksetzen der Position
Wenn es jedoch zur Laufzeit keine Prozedur verwendet, sollten Sie einen Speicherstrom verwenden
quelle
Da keine der Antworten eine asynchrone Methode zum Kopieren von einem Stream in einen anderen behandelt hat, ist hier ein Muster, das ich erfolgreich in einer Portweiterleitungsanwendung verwendet habe, um Daten von einem Netzwerkstrom in einen anderen zu kopieren. Es fehlt die Ausnahmebehandlung, um das Muster hervorzuheben.
quelle
Für .NET 3.5 und bevor Sie versuchen:
quelle
Einfach und sicher - erstellen Sie einen neuen Stream aus der Originalquelle:
quelle