I have a StreamReader
object that I initialized with a stream, now I want to save this stream to disk (the stream may be a .gif
or .jpg
or .pdf
).
Existing Code:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
I have the encoding type also, which I will need if I store it to SQL Server, correct?
110 Answers
As highlighted by Tilendor in Jon Skeet's answer, streams have a CopyTo
method since .NET 4.
var fileStream = File.Create("C:\\Path\\To\\File"); myOtherObject.InputStream.Seek(0, SeekOrigin.Begin); myOtherObject.InputStream.CopyTo(fileStream); fileStream.Close();
Or with the using
syntax:
using (var fileStream = File.Create("C:\\Path\\To\\File")) { myOtherObject.InputStream.Seek(0, SeekOrigin.Begin); myOtherObject.InputStream.CopyTo(fileStream); }
7You must not use StreamReader
for binary files (like gifs or jpgs). StreamReader
is for text data. You will almost certainly lose data if you use it for arbitrary binary data. (If you use Encoding.GetEncoding(28591) you will probably be okay, but what's the point?)
Why do you need to use a StreamReader
at all? Why not just keep the binary data as binary data and write it back to disk (or SQL) as binary data?
EDIT: As this seems to be something people want to see... if you do just want to copy one stream to another (e.g. to a file) use something like this:
/// <summary> /// Copies the contents of input to output. Doesn't close either stream. /// </summary> 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); } }
To use it to dump a stream to a file, for example:
using (Stream file = File.Create(filename)) { CopyStream(input, file); }
Note that Stream.CopyTo
was introduced in .NET 4, serving basically the same purpose.
public void CopyStream(Stream stream, string destPath) { using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write)) { stream.CopyTo(fileStream); } }
4private void SaveFileStream(String path, Stream stream) { var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); stream.CopyTo(fileStream); fileStream.Dispose(); }
3I don't get all of the answers using CopyTo
, where maybe the systems using the app might not have been upgraded to .NET 4.0+. I know some would like to force people to upgrade, but compatibility is also nice, too.
Another thing, I don't get using a stream to copy from another stream in the first place. Why not just do:
byte[] bytes = myOtherObject.InputStream.ToArray();
Once you have the bytes, you can easily write them to a file:
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(); } }
This code works as I've tested it with a .jpg
file, though I admit I have only used it with small files (less than 1 MB). One stream, no copying between streams, no encoding needed, just write the bytes! No need to over-complicate things with StreamReader
if you already have a stream you can convert to bytes
directly with .ToArray()
!
Only potential downsides I can see in doing it this way is if there's a large file you have, having it as a stream and using .CopyTo()
or equivalent allows FileStream
to stream it instead of using a byte array and reading the bytes one by one. It might be slower doing it this way, as a result. But it shouldn't choke since the .Write()
method of the FileStream
handles writing the bytes, and it's only doing it one byte at a time, so it won't clog memory, except that you will have to have enough memory to hold the stream as a byte[]
object. In my situation where I used this, getting an OracleBlob
, I had to go to a byte[]
, it was small enough, and besides, there was no streaming available to me, anyway, so I just sent my bytes to my function, above.
Another option, using a stream, would be to use it with Jon Skeet's CopyStream
function that was in another post - this just uses FileStream
to take the input stream and create the file from it directly. It does not use File.Create
, like he did (which initially seemed to be problematic for me, but later found it was likely just a VS bug...).
/// <summary> /// Copies the contents of input to output. Doesn't close either stream. /// </summary> 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(); }
4Here's an example that uses proper usings and implementation of idisposable:
static void WriteToFile(string sourceFile, string destinationfile, bool append = true, int bufferSize = 4096) { using (var sourceFileStream = new FileStream(sourceFile, FileMode.OpenOrCreate)) { using (var destinationFileStream = new FileStream(destinationfile, FileMode.OpenOrCreate)) { while (sourceFileStream.Position < sourceFileStream.Length) { destinationFileStream.WriteByte((byte)sourceFileStream.ReadByte()); } } } }
...and there's also this
public static void WriteToFile(Stream stream, string destinationFile, int bufferSize = 4096, FileMode mode = FileMode.OpenOrCreate, FileAccess access = FileAccess.ReadWrite, FileShare share = FileShare.ReadWrite) { using (var destinationFileStream = new FileStream(destinationFile, mode, access, share)) { while (stream.Position < stream.Length) { destinationFileStream.WriteByte((byte)stream.ReadByte()); } } }
The key is understanding the proper use of using (which should be implemented on the instantiation of the object that implements idisposable as shown above), and having a good idea as to how the properties work for streams. Position is literally the index within the stream (which starts at 0) that is followed as each byte is read using the readbyte method. In this case I am essentially using it in place of a for loop variable and simply letting it follow through all the way up to the length which is LITERALLY the end of the entire stream (in bytes). Ignore in bytes because it is practically the same and you will have something simple and elegant like this that resolves everything cleanly.
Keep in mind, too, that the ReadByte method simply casts the byte to an int in the process and can simply be converted back.
I'm gonna add another implementation I recently wrote to create a dynamic buffer of sorts to ensure sequential data writes to prevent massive overload
private void StreamBuffer(Stream stream, int buffer) { using (var memoryStream = new MemoryStream()) { stream.CopyTo(memoryStream); var memoryBuffer = memoryStream.GetBuffer(); for (int i = 0; i < memoryBuffer.Length;) { var networkBuffer = new byte[buffer]; for (int j = 0; j < networkBuffer.Length && i < memoryBuffer.Length; j++) { networkBuffer[j] = memoryBuffer[i]; i++; } //Assuming destination file destinationFileStream.Write(networkBuffer, 0, networkBuffer.Length); } } }
The explanation is fairly simple: we know that we need to keep in mind the entire set of data we wish to write and also that we only want to write certain amounts, so we want the first loop with the last parameter empty (same as while). Next, we initialize a byte array buffer that is set to the size of what's passed, and with the second loop we compare j to the size of the buffer and the size of the original one, and if it's greater than the size of the original byte array, end the run.
1Why not use a FileStream object?
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); } }
4//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<Char> while Stream is an IEnumbable<byte>. 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<byte> to IEnumerable<Char> can loose bytes or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance CurrentEncoding. */
1Another option is to get the stream to a byte[]
and use File.WriteAllBytes
. This should do:
using (var stream = new MemoryStream()) { input.CopyTo(stream); File.WriteAllBytes(file, stream.ToArray()); }
Wrapping it in an extension method gives it better naming:
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. }
1public 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); } } }
2ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJoa2ppZW5%2FcLTOsGSdp12eerSt1Z5kmmWjqb%2Bmrcxmq6hlkWKzqrjEZqCnZZM%3D