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

现有代码:

StreamReader sr = new StreamReader(myOtherObject.InputStream);

我需要将其保存到磁盘(我有文件名)。将来我可能希望将其存储到SQL Server。

我还有编码类型,如果我将其存储到SQL Server,我将需要它,对吗?


当前回答

我没有使用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();
    }
}

虽然我承认我只在小文件(小于1MB)中使用过,但这段代码的工作原理与我用.jpg文件测试的一样。一个流,无需在流之间复制,无需编码,只需写入字节!如果您已经有了可以直接使用.ToArray()转换为字节的流,那么无需使用StreamReader使事情变得过于复杂!

我认为这样做的唯一潜在缺点是,如果有一个大文件,将其作为流,并使用.CopyTo()或等效方法,FileStream可以将其流化,而不是使用字节数组并逐个读取字节。结果,这样做可能会慢一些。但是它不应该阻塞,因为FileStream的.Write()方法处理字节的写入,并且它一次只写一个字节,所以它不会阻塞内存,除非您必须有足够的内存将流作为byte[]对象保存。在我使用这个的情况下,获得OracleBlob时,我必须使用一个字节[],它足够小,而且,无论如何,我没有可用的流,所以我只将字节发送到我的函数,如上所述。

另一种选择,使用流,是将它与Jon Skeet在另一篇文章中的CopyStream函数一起使用-这只是使用FileStream获取输入流并直接从中创建文件。它不像他那样使用File.Create(最初对我来说似乎有问题,但后来发现很可能只是一个VS bug…)。

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

其他回答

为什么不使用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);
     }
}
public void CopyStream(Stream stream, string destPath)
{
  using (var fileStream = new FileStream(destPath, FileMode.Create, FileAccess.Write))
  {
    stream.CopyTo(fileStream);
  }
}

您不能将StreamReader用于二进制文件(如gifs或jpgs)。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是在.NET4中引入的,其目的基本相同。

另一个选项是将流设置为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);
        }
    }
}