2009-01-04 70 views
562

我有一個StreamReader對象,我用一個流初始化,現在我想將這個流保存到磁盤(流可能是.gif.jpg.pdf)。如何將流保存到C#文件中?

現有代碼:

StreamReader sr = new StreamReader(myOtherObject.InputStream); 
  1. 我需要這個保存到磁盤(我有文件名)。
  2. 將來我可能希望將其存儲到SQL Server中。

我也有編碼類型,如果將它存儲到SQL Server中,我將需要它嗎?

回答

453

不得對於二進制文件(如gif或jpgs)使用StreamReaderStreamReader用於文本數據。如果您將其用於任意二進制數據,您將幾乎當然會丟失數據。 (如果你使用Encoding.GetEncoding(28591)你可能會沒事,但是有什麼意義?)

爲什麼你需要使用一個StreamReader?爲什麼不把二進制數據保存爲二進制數據並將其作爲二進制數據寫回到磁盤(或SQL)?

編輯:由於這似乎是人們想要的東西,看看......如果你只想一個流複製到另一個(如一個文件),可以這麼用:

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

用它來轉儲流的文件,例如:

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

注意Stream.CopyTo .NET 4的引入,服務基本上是相同的目的。

+5

這似乎是這樣一個常見的情況,我很驚訝它不在.NET中。我發現創建字節數組的大小與整個文件的大小有關,這可能會導致大文件出現問題。 – Tilendor 2010-12-07 17:00:27

+74

@Tilendor:它在.NET 4中作爲擴展方法存在。(CopyTo) – 2010-12-07 17:17:08

+28

我不認爲它是擴展方法,但它是Stream類中的新增方法。 – Kugel 2011-01-24 21:43:14

6

爲什麼不使用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); 
    } 
} 
731

正如喬恩斯基特的回答強調了Tilendor,流有因爲.NET一個CopyTo方法4.

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

或者與using語法:

using (var fileStream = File.Create("C:\\Path\\To\\File")) 
{ 
    myOtherObject.InputStream.Seek(0, SeekOrigin.Begin); 
    myOtherObject.InputStream.CopyTo(fileStream); 
} 
9
//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. 
*/ 
5
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); 
     } 
    } 
} 
52
public void CopyStream(Stream stream, string destPath) 
{ 
    using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write)) 
    { 
    stream.CopyTo(fileStream); 
    } 
} 
15
private void SaveFileStream(String path, Stream stream) 
{ 
    var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write); 
    stream.CopyTo(fileStream); 
    fileStream.Dispose(); 
} 
4

另一個選項是將流傳送到byte[]並使用File.WriteAllBytes。這應該這樣做:

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

在擴展方法結束語給它更好的命名:

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. 
} 
5

我不使用CopyTo,得到所有的答案在哪裏,也許使用的應用程序的系統可能不已經升級到.NET 4.0+。我知道有些人想強制人們升級,但兼容性也很好。

另一件事,我沒有得到使用流首先從另一個流複製。爲什麼不只是做:

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

一旦你的字節,可以輕鬆地將它們寫入文件:

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

此代碼的工作,因爲我已經有一個.jpg文件進行了測試,雖然我承認我只用它與小文件(小於1 MB)。一個流,不在流之間複製,不需要編碼,只需寫入字節!如果您已經有一個流,您可以直接使用.ToArray()直接轉換爲bytes,那麼無需過度複雜化StreamReader

唯一潛在的缺點,我可以做這樣看問題是,如果有你有一個大的文件,有它作爲流和使用.CopyTo()或相當於允許FileStream,而不是流它使用一個字節數組和讀取的字節之一一個。因此,這樣做可能會更慢。但它不應該窒息,因爲FileStream.Write()方法處理寫入字節,並且它一次只做一個字節,所以它不會阻塞內存,除非您必須有足夠的內存來容納流作爲byte[]對象。在我使用這個的情況下,得到一個OracleBlob,我不得不去一個byte[],它足夠小,而且,沒有流可用,無論如何,所以我只是發送我的字節到我的函數,上面。

另一種使用流的方法是將其與Jon Skeet的CopyStream函數一起使用,該函數在另一篇文章中僅使用FileStream來接收輸入流並直接從中創建文件。它沒有使用File.Create,就像他一樣(最初似乎對我有問題,但後來發現它可能只是一個VS錯誤...)。

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