Come faccio a copiare i contenuti di uno stream su un altro?

Qual è il modo migliore per copiare i contenuti di uno stream su un altro? Esiste un metodo di utilità standard per questo?

Da .NET 4.5 in poi, esiste il metodo Stream.CopyToAsync

 input.CopyToAsync(output); 

Ciò restituirà un’attività che può essere proseguita quando completata, in questo modo:

 await input.CopyToAsync(output) // Code from here on will be run in a continuation. 

Si noti che a seconda di dove viene effettuata la chiamata a CopyToAsync , il codice che segue potrebbe continuare o meno sullo stesso thread che lo ha chiamato.

Il SynchronizationContext che è stato catturato durante la chiamata await determinerà su quale thread verrà eseguita la continuazione.

Inoltre, questa chiamata (e questo è un dettaglio di implementazione sobject a modifiche) continua a leggere e scrivere sequenze (semplicemente non spreca un thread che blocca il completamento dell’I / O).

Da .NET 4.0 in poi, c’è il metodo Stream.CopyTo

 input.CopyTo(output); 

Per .NET 3.5 e versioni precedenti

Non c’è nulla di utile nel framework per aiutare con questo; devi copiare il contenuto manualmente, in questo modo:

 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); } } 

MemoryStream ha .WriteTo (outstream);

e .NET 4.0 ha .CopyTo su object stream normale.

.NET 4.0:

 instream.CopyTo(outstream); 

Io uso i seguenti metodi di estensione. Hanno sovraccarichi ottimizzati per quando uno stream è un MemoryStream.

  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); } 

Le domande di base che differenziano le implementazioni di “CopyStream” sono:

  • dimensione del buffer di lettura
  • dimensione delle scritture
  • Possiamo usare più di un thread (scrivere mentre stiamo leggendo).

Le risposte a queste domande si traducono in implementazioni molto diverse di CopyStream e dipendono dal tipo di stream che hai e da cosa stai cercando di ottimizzare. L’implementazione “migliore” avrebbe anche bisogno di sapere quale hardware specifico stessero leggendo e scrivendo i flussi.

Esiste, in realtà, un modo meno pesante di fare una copia in streaming. Prendi nota, tuttavia, che ciò implica che è ansible memorizzare l’intero file in memoria. Non tentare di usarlo se stai lavorando con file che vanno nelle centinaia di megabyte o più, senza caucanvas.

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

NOTA: potrebbero esserci anche alcuni problemi relativi ai dati binari e alle codifiche dei caratteri.

Sfortunatamente, non esiste una soluzione davvero semplice. Puoi provare qualcosa del genere:

 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(); 

Ma il problema è che una diversa implementazione della class Stream potrebbe comportarsi diversamente se non c’è nulla da leggere. Un stream che legge un file da un hard disk locale probabilmente bloccherà fino a quando l’operazione di lettura non avrà letto abbastanza dati dal disco per riempire il buffer e restituirà solo meno dati se raggiunge la fine del file. D’altra parte, una lettura in streaming dalla rete potrebbe restituire meno dati anche se ci sono più dati da ricevere.

Controllare sempre la documentazione della specifica class di stream che si sta utilizzando prima di utilizzare una soluzione generica.

Potrebbe esserci un modo per farlo in modo più efficiente, a seconda del tipo di stream con cui stai lavorando. Se è ansible convertire uno o entrambi i flussi in un MemoryStream, è ansible utilizzare il metodo GetBuffer per lavorare direttamente con un array di byte che rappresenta i dati. Ciò consente di utilizzare metodi come Array.CopyTo, che riassumono tutti i problemi sollevati da fryguybob. Puoi semplicemente fidarti di .NET per conoscere il modo ottimale per copiare i dati.

se si desidera che un procdure copi uno stream su un altro quello che il nick postato va bene ma manca il reset della posizione, dovrebbe essere

 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 } 

ma se in runtime non si utilizza una procedura, si deve usare il stream di memoria

 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 

Poiché nessuna delle risposte ha coperto un modo asincrono di copiare da uno stream a un altro, ecco uno schema che ho utilizzato con successo in un’applicazione di inoltro porta per copiare i dati da un stream di rete a un altro. Manca la gestione delle eccezioni per enfatizzare il modello.

 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); } 

.NET Framework 4 introduce un nuovo metodo “CopyTo” di Stream Class of System.IO namespace. Usando questo metodo possiamo copiare un stream in un altro stream di diverse classi di stream.

Ecco un esempio per questo.

  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("

"); Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString())); Response.Write("

");

Per .NET 3.5 e prima prova:

 MemoryStream1.WriteTo(MemoryStream2); 

Facile e sicuro: crea un nuovo stream dalla fonte originale:

  MemoryStream source = new MemoryStream(byteArray); MemoryStream copy = new MemoryStream(byteArray);