如何在 C# 中将流保存到文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/411592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I save a stream to a file in C#?
提问by Loadman
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
).
我有一个StreamReader
用流初始化的对象,现在我想将此流保存到磁盘(流可能是 a.gif
或.jpg
或.pdf
)。
Existing Code:
现有代码:
StreamReader sr = new StreamReader(myOtherObject.InputStream);
- I need to save this to disk (I have the filename).
- In the future I may want to store this to SQL Server.
- 我需要将其保存到磁盘(我有文件名)。
- 将来我可能想将其存储到 SQL Server。
I have the encoding type also, which I will need if I store it to SQL Server, correct?
我也有编码类型,如果我将它存储到 SQL Server,我将需要它,对吗?
回答by Jon Skeet
You must notuse StreamReader
for binary files (like gifs or jpgs). StreamReader
is for textdata. You will almost certainlylose 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?)
你不能使用StreamReader
二进制文件(如gif或JPG格式)。StreamReader
用于文本数据。如果将其用于任意二进制数据,几乎肯定会丢失数据。(如果你使用 Encoding.GetEncoding(28591) 你可能会没事,但有什么意义呢?)
Why do you need to use a StreamReader
at all? Why not just keep the binary data asbinary data and write it back to disk (or SQL) as binary data?
为什么你需要使用 a StreamReader
?为什么不将二进制数据保留为二进制数据并将其作为二进制数据写回磁盘(或 SQL)?
EDIT: As this seems to be something people want to see... if you dojust 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.
请注意,它Stream.CopyTo
是在 .NET 4 中引入的,用于基本相同的目的。
回答by Adrian
Why not use a FileStream object?
为什么不使用 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);
}
}
回答by Antoine Leclair
As highlighted by Tilendor in Jon Skeet's answer, streams have a CopyTo
method since .NET 4.
正如 Tilendor 在 Jon Skeet 的回答中所强调的那样,CopyTo
从 .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
语法:
using (var fileStream = File.Create("C:\Path\To\File"))
{
myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
myOtherObject.InputStream.CopyTo(fileStream);
}
回答by George
//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.
*/
回答by Angelo
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);
}
}
}
回答by Darren Corbett
public void CopyStream(Stream stream, string destPath)
{
using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
{
stream.CopyTo(fileStream);
}
}
回答by jhonjairoroa87
private void SaveFileStream(String path, Stream stream)
{
var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
fileStream.Dispose();
}
回答by nawfal
Another option is to get the stream to a byte[]
and use File.WriteAllBytes
. This should do:
另一种选择是将流获取到 abyte[]
并使用File.WriteAllBytes
. 这应该做:
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.
}
回答by vapcguy
I 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.
我没有使用 得到所有答案CopyTo
,其中使用该应用程序的系统可能尚未升级到 .NET 4.0+。我知道有些人想强迫人们升级,但兼容性也很好。
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()
!
这段代码在我用.jpg
文件测试时有效,但我承认我只将它用于小文件(小于 1 MB)。一个流,流之间无需复制,无需编码,只需写入字节即可!StreamReader
如果您已经有一个可以bytes
直接转换为的流,则无需使事情变得过于复杂.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.
我这样做的唯一潜在缺点是,如果您拥有一个大文件,将其作为流并使用.CopyTo()
或等效允许FileStream
流式传输它而不是使用字节数组并逐个读取字节。因此,这样做可能会更慢。但是它不应该阻塞,因为句柄的.Write()
方法FileStream
写入字节,并且一次只执行一个字节,因此它不会阻塞内存,除非您必须有足够的内存来保存流作为byte[]
对象。在我使用它的情况下,得到一个OracleBlob
,我不得不去一个byte[]
,它足够小,此外,无论如何,我没有可用的流媒体,所以我只是将我的字节发送到我的函数,上面。
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...).
使用流的另一种选择是将它与CopyStream
另一篇文章中的Jon Skeet 的函数一起使用- 这只是用于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();
}
回答by Andrew Ramshaw
Here's an example that uses proper usings and implementation of idisposable:
这是一个使用正确使用和实现 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(FileStream 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.
关键是理解 using 的正确使用(应该在实现 idisposable 的对象的实例化上实现,如上所示),并很好地了解属性如何为流工作。位置实际上是流中的索引(从 0 开始),在使用 readbyte 方法读取每个字节时跟随该索引。在这种情况下,我基本上使用它代替 for 循环变量,并简单地让它一直跟踪到长度,这实际上是整个流的结尾(以字节为单位)。以字节为单位忽略,因为它实际上是相同的,您将拥有像这样简单而优雅的东西,可以干净地解决所有问题。
Keep in mind, too, that the ReadByte method simply casts the byte to an int in the process and can simply be converted back.
还要记住,ReadByte 方法只是在进程中将字节转换为 int 并且可以简单地转换回来。
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.
解释相当简单:我们知道我们需要记住我们希望写入的整个数据集,而且我们只想写入一定数量的数据,因此我们希望第一个循环的最后一个参数为空(与 while 相同) )。接下来,我们初始化一个字节数组缓冲区,该缓冲区设置为传递的大小,在第二个循环中,我们将 j 与缓冲区的大小和原始大小进行比较,如果它大于原始大小字节数组,结束运行。