¿Cómo guardo una secuencia en un archivo en C #?

Tengo un objeto StreamReader que inicié con una secuencia, ahora quiero guardar esta secuencia en el disco (la secuencia puede ser .gif o .jpg o .pdf ).

Código existente:

 StreamReader sr = new StreamReader(myOtherObject.InputStream); 
  1. Necesito guardar esto en el disco (tengo el nombre del archivo).
  2. En el futuro, es posible que desee almacenar esto en SQL Server.

También tengo el tipo de encoding, que necesitaré si lo almaceno en SQL Server, ¿correcto?

Como destacó Tilendor en la respuesta de Jon Skeet, las transmisiones tienen un método CopyTo desde .NET 4.

 var fileStream = File.Create("C:\\Path\\To\\File"); myOtherObject.InputStream.Seek(0, SeekOrigin.Begin); myOtherObject.InputStream.CopyTo(fileStream); fileStream.Close(); 

O con la syntax que using :

 using (var fileStream = File.Create("C:\\Path\\To\\File")) { myOtherObject.InputStream.Seek(0, SeekOrigin.Begin); myOtherObject.InputStream.CopyTo(fileStream); } 

No debe usar StreamReader para archivos binarios (como gifs o jpgs). StreamReader es para datos de texto . Es casi seguro que perderá datos si los usa para datos binarios arbitrarios. (Si usa Encoding.GetEncoding (28591) probablemente estará bien, pero ¿cuál es el punto?)

¿Por qué necesitas usar un StreamReader en absoluto? ¿Por qué no mantener los datos binarios como datos binarios y escribirlos en el disco (o SQL) como datos binarios?

EDITAR: como esto parece ser algo que la gente quiere ver … si solo quiere copiar una secuencia a otra (por ejemplo, a un archivo) use algo como esto:

 ///  /// Copies the contents of input to output. Doesn't close either stream. ///  public static void CopyStream(Stream input, Stream output) { byte[] buffer = new byte[8 * 1024]; int len; while ( (len = input.Read(buffer, 0, buffer.Length)) > 0) { output.Write(buffer, 0, len); } } 

Para usarlo para volcar una secuencia a un archivo, por ejemplo:

 using (Stream file = File.Create(filename)) { CopyStream(input, file); } 

Tenga en cuenta que Stream.CopyTo se introdujo en .NET 4, que básicamente tiene el mismo objective.

 public void CopyStream(Stream stream, string destPath) { using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write)) { stream.CopyTo(fileStream); } } 
 private void SaveFileStream(String path, Stream stream) { var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); stream.CopyTo(fileStream); fileStream.Dispose(); } 
 //If you don't have .Net 4.0 :) public void SaveStreamToFile(Stream stream, string filename) { using(Stream destination = File.Create(filename)) Write(stream, destination); } //Typically I implement this Write method as a Stream extension method. //The framework handles buffering. public void Write(Stream from, Stream to) { for(int a = from.ReadByte(); a != -1; a = from.ReadByte()) to.WriteByte( (byte) a ); } /* Note, StreamReader is an IEnumerable while Stream is an IEnumbable. The distinction is significant such as in multiple byte character encodings like Unicode used in .Net where Char is one or more bytes (byte[n]). Also, the resulting translation from IEnumerable to IEnumerable can loose bytes or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance CurrentEncoding. */ 

¿Por qué no usar un objeto FileStream?

 public void SaveStreamToFile(string fileFullPath, Stream stream) { if (stream.Length == 0) return; // Create a FileStream object to write a stream to a file using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length)) { // Fill the bytes[] array with the stream data byte[] bytesInStream = new byte[stream.Length]; stream.Read(bytesInStream, 0, (int)bytesInStream.Length); // Use FileStream object to write to the specified file fileStream.Write(bytesInStream, 0, bytesInStream.Length); } } 

No obtengo todas las respuestas usando CopyTo , donde quizás los sistemas que usan la aplicación no se hayan actualizado a .NET 4.0+. Sé que a algunos les gustaría obligar a las personas a actualizarse, pero la compatibilidad también es agradable.

Otra cosa, en primer lugar, no consigo usar una secuencia para copiar desde otra secuencia. Por qué no solo hacer:

 byte[] bytes = myOtherObject.InputStream.ToArray(); 

Una vez que tenga los bytes, puede escribirlos fácilmente en un archivo:

 public static void WriteFile(string fileName, byte[] bytes) { string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!path.EndsWith(@"\")) path += @"\"; if (File.Exists(Path.Combine(path, fileName))) File.Delete(Path.Combine(path, fileName)); using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write) { fs.Write(bytes, 0, (int)bytes.Length); fs.Close(); } } 

Este código funciona como lo he probado con un archivo .jpg , aunque admito que solo lo he usado con archivos pequeños (menos de 1 MB). Una secuencia, sin copia entre las secuencias, sin encoding, ¡simplemente escriba los bytes! No hay necesidad de complicar demasiado las cosas con StreamReader si ya tiene una secuencia que puede convertir a bytes directamente con .ToArray() !

Solo las desventajas potenciales que puedo ver al hacerlo de esta manera es si hay un archivo grande que tiene, como una secuencia y usando .CopyTo() o equivalente permite a FileStream transmitirlo en lugar de usar una matriz de bytes y leer los bytes uno por uno. Puede ser más lento hacerlo de esta manera, como resultado. Pero no debería ahogarse, ya que el método .Write() del FileStream maneja la escritura de los bytes, y solo lo hace un byte a la vez, por lo que no obstruirá la memoria, excepto que tendrá que tener memoria suficiente para mantener la secuencia como un objeto byte[] . En mi situación donde utilicé esto, obtener un OracleBlob , tuve que ir a un byte[] , era lo suficientemente pequeño, y además, no había transmisión disponible para mí, de todos modos, así que simplemente envié mis bytes a mi función, encima.

Otra opción, usar una secuencia, sería usarla con la función CopyStream Jon Skeet que estaba en otra publicación; esto solo utiliza FileStream para tomar la secuencia de entrada y crear el archivo directamente desde allí. No usa File.Create , como lo hizo él (que inicialmente parecía ser problemático para mí, pero luego descubrí que probablemente solo era un error de VS …).

 ///  /// Copies the contents of input to output. Doesn't close either stream. ///  public static void CopyStream(Stream input, Stream output) { byte[] buffer = new byte[8 * 1024]; int len; while ( (len = input.Read(buffer, 0, buffer.Length)) > 0) { output.Write(buffer, 0, len); } } public static void WriteFile(string fileName, Stream inputStream) { string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!path.EndsWith(@"\")) path += @"\"; if (File.Exists(Path.Combine(path, fileName))) File.Delete(Path.Combine(path, fileName)); using (FileStream fs = new FileStream(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.Write) { CopyStream(inputStream, fs); } inputStream.Close(); inputStream.Flush(); } 
 public void testdownload(stream input) { byte[] buffer = new byte[16345]; using (FileStream fs = new FileStream(this.FullLocalFilePath, FileMode.Create, FileAccess.Write, FileShare.None)) { int read; while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { fs.Write(buffer, 0, read); } } } 

Otra opción es obtener la secuencia en un byte[] y usar File.WriteAllBytes . Esto debería hacer:

 using (var stream = new MemoryStream()) { input.CopyTo(stream); File.WriteAllBytes(file, stream.ToArray()); } 

Envolverlo en un método de extensión le da un mejor nombre:

 public void WriteTo(this Stream input, string file) { //your fav write method: using (var stream = File.Create(file)) { input.CopyTo(stream); } //or using (var stream = new MemoryStream()) { input.CopyTo(stream); File.WriteAllBytes(file, stream.ToArray()); } //whatever that fits. }