Wie lässt sich der Inhalt eines Streams am besten in einen anderen kopieren? Gibt es eine Standardmethode für diese Aufgabe?
Antworten
Zu viele Anzeigen?Die grundlegenden Fragen, die die Implementierungen von "CopyStream" unterscheiden, sind:
- Größe des Lesepuffers
- Umfang 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, welche Art von Streams Sie haben und was Sie zu optimieren versuchen. Die "beste" Implementierung müsste sogar wissen, auf welcher spezifischen Hardware die Streams gelesen und geschrieben wurden.
Leider gibt es keine wirklich einfache Lösung. Sie können so etwas versuchen:
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 verschiedene Implementierungen der Stream-Klasse unterschiedlich verhalten können, wenn es nichts zu lesen gibt. Ein Stream, der eine Datei von einer lokalen Festplatte liest, wird wahrscheinlich blockieren, bis die Leseoperation genug Daten von der Festplatte gelesen hat, um den Puffer zu füllen, und nur dann weniger Daten zurückgeben, wenn er das Ende der Datei erreicht. Ein Stream, der aus dem Netz liest, gibt dagegen 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.
Je nachdem, mit welcher Art von Stream Sie arbeiten, gibt es vielleicht eine Möglichkeit, dies effizienter zu tun. 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. Dadurch können Sie Methoden wie Array.CopyTo verwenden, die alle von fryguybob angesprochenen Probleme beseitigen. Sie können einfach darauf vertrauen, dass .NET den optimalen Weg zum Kopieren der Daten kennt.
Wenn Sie einen Prozess zum Kopieren eines Streams in einen anderen wollen, ist der von Nick gepostete gut, aber es fehlt das Zurücksetzen der Position, es sollte 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
}
aber wenn es in der Laufzeit nicht mit einer Prozedur Sie shpuld verwenden Speicherstrom
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
Da keine der Antworten eine asynchrone Art des Kopierens von einem Stream in einen anderen behandelt hat, hier ein Muster, das ich erfolgreich in einer Portweiterleitungsanwendung verwendet habe, um Daten von einem Netzwerkstream in einen anderen zu kopieren. Es fehlt die 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);
}