如何将流保存到C#中的文件?

时间:2009-01-04 20:04:40

标签: c# .net stream

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

现有代码:

.pdf
  1. 我需要将其保存到磁盘(我有文件名)。
  2. 将来我可能希望将其存储到SQL Server。
  3. 我也有编码类型,如果我将它存储到SQL Server,我需要它,对吗?

10 个答案:

答案 0 :(得分:836)

正如Tilendor在Jon Skeet的回答中强调的那样,自.NET 4以来,流有一个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);
}

答案 1 :(得分:493)

不得使用StreamReader二进制文件(如gifs或jpgs)。 StreamReader用于 text 数据。如果将它用于任意二进制数据,您几乎肯定会丢失数据。 (如果你使用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);
}

请注意,{4}在.NET 4中引入,基本上用于相同的目的。

答案 2 :(得分:65)

public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}

答案 3 :(得分:22)

private void SaveFileStream(String path, Stream stream)
{
    var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write);
    stream.CopyTo(fileStream);
    fileStream.Dispose();
}

答案 4 :(得分:10)

我没有使用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)。一个流,流之间没有复制,不需要编码,只需写入字节!如果您已经拥有可以直接使用StreamReader转换为bytes的流,则无需使用.ToArray()过度复杂化!

我只能通过这种方式看到的潜在缺点是,如果您拥有一个大型文件,将其作为流并使用.CopyTo()或等效允许FileStream来传输它使用字节数组并逐个读取字节。结果这样做可能会慢一些。但它不应该扼要,因为.Write()的{​​{1}}方法处理写字节,并且它一次只做一个字节,所以它不会won阻塞内存,除了你必须有足够的内存来将流保存为FileStream对象。在我使用它的情况下,得到一个byte[],我不得不去OracleBlob,它足够小,而且,无论如何,我都没有可用的流媒体,所以我只是发送了我的字节到我的函数,上面。

使用流的另一个选择是将它与Jon Skeet的另一篇帖子中的byte[]函数一起使用 - 这只是使用CopyStream来获取输入流并创建从中直接提交文件。它没有使用FileStream,就像他一样(最初对我来说似乎有问题,但后来发现它可能只是一个VS错误......)。

File.Create

答案 5 :(得分:8)

//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.
*/

答案 6 :(得分:5)

为什么不使用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);
     }
}

答案 7 :(得分:4)

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

答案 8 :(得分: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.
}

答案 9 :(得分:0)

以下是使用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(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());
            }
        }
    }

关键是要理解use的正确用法(应该在实现如上所示的可实现对象的实例上实现),并对属性如何用于流工作有一个好主意。位置实际上是流中的索引(从0开始),使用readbyte方法读取每个字节时遵循该索引。在这种情况下,我实际上是在用它代替for循环变量,并简单地让它一直跟踪直到整个流的末尾(以字节为单位)。忽略字节,因为它实际上是相同的,并且您将拥有简单而优雅的东西,可以清晰地解析所有内容。

还请记住,ReadByte方法只是在处理过程中将字节强制转换为int,并且可以简单地转换回该字节。

我将添加我最近编写的另一种实现,以创建各种动态缓冲区,以确保顺序写入数据以防止大量过载

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与缓冲区的大小以及原始缓冲区的大小进行比较,如果它大于原始缓冲区的大小,则对其进行比较。字节数组,结束运行。