我有一个StreamReader
对象,它使用流进行了初始化,现在我想将此流保存到磁盘上(该流可能是.gif
、.jpg
或.pdf
文件)。
现有代码:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
- 我需要将这个保存到磁盘(我有文件名)。
- 将来我可能想将其存储到SQL Server中。
我也有编码类型,如果我要将其存储到SQL Server中,我将需要它,对吗?
正如Tilendor在Jon Skeet的回答中所强调的那样,自从.NET 4以来,流(Streams)就拥有了一个CopyTo
方法。
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);
}
如果你不想复制整个流,请调用 Seek
,以确保你已经在开头。
fileStream
上写入的权限。 - Antoine Leclair.Seek(0, SeekOrigin.Begin)
而不是.Position = 0
?因为两者似乎都可以做同样的事情。 - Martin Schneider.Position = 0
,我也更喜欢这种语法。 - Antoine Leclair您绝不能使用StreamReader
来处理二进制文件(如gif或jpg)。StreamReader
专用于处理文本数据,如果用于任意二进制数据,则几乎肯定会丢失数据。(如果使用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
,基本上具有相同的功能。public void CopyStream(Stream stream, string destPath)
{
using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
stream
对象放入 using(){}
括号中。你的方法没有创建该流对象,因此也不应该对其进行释放。 - LarsTechFileStream
而不是 using
,否则它会一直保持打开状态,直到被垃圾回收。 - Pavel ChikulaevFile.WriteAllBytes(destinationFilePath, input.ToArray());
。在我的情况中,input
是从 ZipArchive
中获取的一个 MemoryStream
。 - SNagstream
可能不在开头,请将 stream.Position = 0;
作为此方法的第一行。 - ToolmakerSteveprivate void SaveFileStream(String path, Stream stream)
{
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
fileStream.Dispose();
}
File.WriteAllBytes(destinationFilePath, input.ToArray());
。在我的情况下,input
是来自于ZipArchive
中的MemoryStream
。 - SNagstream.Seek(0, SeekOrigin.Begin);
- Nathan Billsstream.Position = 0;
是移动到流的开头的另一种语法形式。 - ToolmakerSteve使用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
文件,不过我只用过小文件(小于1MB)。一个流,没有在流之间复制,也不需要编码,只需写入字节!如果你已经有一个流,可以直接使用 .ToArray()
将其转换为 bytes
,就不需要使用 StreamReader
进行过度处理了!唯一的潜在缺点是,如果你有一个大文件,并且使用 .CopyTo()
或相似的方法,则 FileStream
可以通过流式传输而不是使用一个字节数组逐个读取字节。因此,这种方式可能会更慢。但它不会崩溃,因为 FileStream
的 .Write()
方法处理写入字节,而且它只是一次写一个字节,所以不会占用内存,除非你必须有足够的内存来将流作为 byte[]
对象保存。在我使用它的情况下,我得到了一个 OracleBlob
,我必须转换为 byte[]
,它很小,而且除此之外,对我来说没有可用的流,所以我只是把我的字节发送给了上面的函数。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();
}
Close
,因为使用了using()
。 - Alex78191inputStream.Close()
,请再看一遍 - inputStream
是作为变量发送的。using
应该是在 path+filename
的输出流上。如果你在谈论 using
中间的 fs.Close()
,那么抱歉,你是正确的,我已经将其删除。 - vapcguy.Close()
,因为 .Flush()
也会关闭它,并且我想要同时执行这两个命令。 - vapcguyStream.ToArray()
方法,那是从哪里来的?至于为什么要使用Stream
而不是直接使用byte[]
,我会问为什么在你已经有一个Stream
的情况下还要引入一个(中间)byte[]
?你提到了将整个Stream
缓冲到byte[]
的缺点——内存影响——但好处是什么?此外,这个答案一开始就质疑了Stream
-to-Stream
复制的有用性,但随后又建议使用CopyStream()
,它的实现方式与CopyTo()
相同。 - Lance U. MatthewsStream.ToArray()
了?看代码。它在那之前就有 InputStream
!它是基于该方法,而不是原始流。字节对象可以更容易地进行操作并且可以被存储。这就是好处所在。流必须先被写入,这样才能对底层数据进行操作。所以如果你必须这样做,为什么还要使用它呢?只需使用/复制它作为一个字节对象即可。而且,CopyStream()
是将其写入到一个 byte[]
中,而不是另一个流!所以,不,它不像 CopyTo()
那样简单。你完全没有理解这些内容,是吗? - vapcguy这是一个使用正确的“using”和实现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());
}
}
}
}
还有这个
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());
}
}
}
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);
}
}
}
这个解释相当简单:我们知道需要记住整个数据集,但只想写入一定数量的数据,所以我们希望第一个循环的最后一个参数为空(与while相同)。接下来,我们初始化一个字节数组缓冲区,其大小设置为传递的大小,并使用第二个循环将j与缓冲区和原始数组的大小进行比较,如果它大于原始字节数组的大小,则结束运行。
为什么不使用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);
}
}
byte[]
分配足够的内存,但我认为将1 GB以上的blob流式传输到文件中是很少见的...除非你有一个保留DVD种子的网站...此外,现在大多数计算机都至少有2 GB的可用RAM...警告是有效的,但我认为这是一个“对于大多数工作来说足够好”的情况。 - vapcguy//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.
*/
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.
}
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);
}
}
}
FileStream
- 很好! - vapcguy