如何在 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 02:21:45  来源:igfitidea点击:

How do I save a stream to a file in C#?

c#.netstream

提问by Loadman

I have a StreamReaderobject that I initialized with a stream, now I want to save this stream to disk (the stream may be a .gifor .jpgor .pdf).

我有一个StreamReader用流初始化的对象,现在我想将此流保存到磁盘(流可能是 a.gif.jpg.pdf)。

Existing Code:

现有代码:

StreamReader sr = new StreamReader(myOtherObject.InputStream);
  1. I need to save this to disk (I have the filename).
  2. In the future I may want to store this to SQL Server.
  1. 我需要将其保存到磁盘(我有文件名)。
  2. 将来我可能想将其存储到 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 StreamReaderfor binary files (like gifs or jpgs). StreamReaderis 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 StreamReaderat 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.CopyTowas 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 CopyTomethod 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 usingsyntax:

或者使用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 .jpgfile, 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 StreamReaderif you already have a stream you can convert to bytesdirectly 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 FileStreamto 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 FileStreamhandles 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 CopyStreamfunction that was in another post - this just uses FileStreamto 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 与缓冲区的大小和原始大小进行比较,如果它大于原始大小字节数组,结束运行。