当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何将流保存到C#中的文件?》经验,为你挑选了8个好方法。

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

现有守则:

StreamReader sr = new StreamReader(myOtherObject.InputStream);

    我需要将其保存到磁盘(我有文件名).

    在将来,我可能希望将其存储到SQL Server.

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



1> Antoine Lecl..:

正如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();

或者使用using语法:

using (var fileStream = File.Create("C:\\Path\\To\\File"))
{
    myOtherObject.InputStream.Seek(0, SeekOrigin.Begin);
    myOtherObject.InputStream.CopyTo(fileStream);
}


请注意,如果您还没有在开头,则必须调用`myOtherObject.InputStream.Seek(0,SeekOrigin.Begin)`或者您不会复制整个流.
如果此输入流是从http连接获得的,那么它将缓冲并下载然后从源写入所有字节?????
我创建了PDF查看器,我正在使用流,一旦我绑定流,当我使用相同的流保存pdf文件,然后不使用"Seek(0,SeekOrigin.Begin)"我将无法保存正确的文档.所以+1提到这个"寻求(0,SeekOrigin.Begin)"

2> Jon Skeet..:

不能使用StreamReader二进制文件(如gifs或jpgs).StreamReader用于文本数据.如果将它用于任意二进制数据,几乎肯定会丢失数据.(如果你使用Encoding.GetEncoding(28591),你可能会没事,但重点是什么?)

为什么你需要使用一个StreamReader?为什么不将二进制数据保存二进制数据并将其作为二进制数据写回磁盘(或SQL)?

编辑:由于这似乎是人们想要的东西,看看......如果你只是要一个流复制到另一个(如一个文件)使用是这样的:

/// 
/// Copies the contents of input to output. Doesn't close either stream.
/// 
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是在.NET 4中引入的,基本上用于相同的目的.


@Tilendor:它作为.NET 4中的扩展方法存在.(CopyTo)
我不认为它是一种扩展方法,但它是Stream类中的新功能.
@Kugel:你是对的,对不起.*我*将它作为实用程序库中的扩展方法,但现在它在Stream本身,我的扩展方法不会被调用.
这似乎是一种常见的情况,我很惊讶它不在.NET中.我看到人们创建的字节数组大小与整个文件大小相同,这可能会导致大文件出现问题.
@Florian:它是相当随意的 - 一个足够小的值可以避免占用太多内存,而且足够大,可以一次传输一个合理的块.可能是16K,32K可能 - 我只是小心不要在大对象堆上结束.
为什么要将缓冲区的大小设置为8192?谢谢

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


你可能不应该把`stream`对象放在`using(){}`括号中.您的方法没有创建流,因此不应该将其丢弃.
相反,您需要将`FileStream`改为使用,否则它将保持打开状态,直到它被垃圾收集.

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


这帮我弄明白我做错了什么.但是,不要忘记移动到流的开头:`stream.Seek(0,SeekOrigin.Begin);`

5> vapcguy..:

我没有得到所有答案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()方法FileStream写入字节而窒息,并且它一次只做一个字节,所以它不会阻塞内存,除了你必须有足够的内存来保存流作为一个byte[]对象.在我使用它的情况下,获得一个OracleBlob,我必须去一个byte[],它足够小,而且,无论如何,我没有可用的流,所以我只是把我的字节发送到我的函数,上面.

使用流的另一个选择是将它与Jon Skeet的CopyStream功能一起使用,该功能位于另一个帖子中 - 这只是FileStream用来获取输入流并直接从中创建文件.它没有File.Create像他那样使用(最初对我来说似乎有问题,但后来发现它可能只是一个VS bug ......).

/// 
/// Copies the contents of input to output. Doesn't close either stream.
/// 
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();
}



6> 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 while Stream is an IEnumbable.
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 to IEnumerable can loose bytes
or insert them (for example, "\n" vs. "\r\n") depending on the StreamReader instance
CurrentEncoding.
*/


逐个字节地复制流(使用ReadByte/WriteByte)将比逐个缓冲区复制慢得多(使用Read(byte [],int,int)/ Write(byte [],int,int)).

7> 小智..:

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


如果输入流长度为1GB怎么办 - 这段代码会尝试分配1GB缓冲区:)
Buthrakaur是对的 - 应该删除这个答案.

8> nawfal..:

另一个选择是将流转到a 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.
}


如果输入太大,则会出现内存不足异常。将内容从输入流复制到文件流的选项要好得多
推荐阅读
有风吹过best
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有