2009-12-29 60 views
2

我看到下面得到该文件到数组,这又作为SQL命令将其插入到BLOB列一个参​​数代码:为什么我需要逐个读取文件缓冲?

using (FileStream fs = new FileStream(soubor,FileMode.Open,FileAccess.Read)) 

int length = (int)fs.Length; 
buffer = new byte[length]; 
int count; 
int sum = 0; 
while ((count = fs.Read(buffer, sum, length - sum)) > 0) 
    sum += count; 

为什么我不能简单地做:

fs.Read(缓冲区,0,长度)以便将文件内容复制到缓冲区中?

感谢

回答

3

由于您的文件可能会非常大,缓冲区有4-32 KB的通常是一个固定的大小。通过这种方式,你知道你并不是一厢情愿地填满你的记忆。

当然,如果你知道你的文件的大小不是太大,或者如果你将内容存储在内存中,没有理由不一次全部读取它。

虽然,如果您想直接将文件内容读入变量,则不需要Stream API。而是使用

File.ReadAllText(...) 

File.ReadAllBytes(...) 
+0

有一个很好的理由不承担* *您可以一次性读取它:在'Stream' API并不能保证它会在一个呼叫读到的一切。 – 2009-12-29 12:20:27

+0

'buffer'显式设置为文件的长度。所以你的第一段没有真正回答这个问题。 – 2009-12-29 12:23:22

4

还有更给它不仅仅是“的文件可能不适合在内存”。为Stream.Read合同明确说:

这种方法的

实现从 电流流读取 最大的数个字节,并将它们存储在缓冲区 开始偏移量。 流内的当前位置是 先读取的字节数;然而,如果发生异常,流 内的当前位置保持不变。实现 返回读取的字节数。仅当 位置当前位于 流的末尾时, 返回值为零。在 没有数据可用的情况下,该实现将 阻塞,直到可以读取至少一个字节的数据 。仅当 流 中没有更多数据且预期不再有更多数据(例如 关闭的套接字或文件结尾)时,只读取返回0。即使未到达流的结尾 , 实现也可以自由返回比请求少的 字节。

请注意最后一句 - 您不能依靠一个电话来Stream.Read来读取所有内容。

FileStream.Read该文档具有类似的警告:

读入 缓冲区的字节总数。如果 字节当前不是 可用,则可能小于 所请求的字节数,如果达到 流的末尾,则可能为零。

对于本地文件系统,我不确定这是否真的会发生 - 但它可以为网络挂载的文件。你想让你的应用变得脆弱吗?

在一个循环看书做事可靠的方法。我个人更喜欢不要求流支持Length财产,或者:

public static byte[] ReadFully(Stream stream) 
{ 
    byte[] buffer = new byte[8192]; 
    using (MemoryStream tmpStream = new MemoryStream()) 
    { 
     int bytesRead; 
     while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) 
     { 
      tmpStream.Write(buffer, 0, bytesRead); 
     } 
     return tmpStream.ToArray(); 
    } 
} 

当长度事先知道,但它是很好的和简单的稍微低效率的。您只需实施一次,将其放入实用程序库中,并在需要时随时拨打电话。如果您确实介意效率损失,则可以使用CanSeek来测试Length属性是否受支持,并在该情况下反复读入单个缓冲区。请注意,尽管在阅读时,流的长度可能会发生变化......

当然,File.ReadAllBytes会更简单,当您只需处理文件而不是一般流。

1

一个简单的fs.Read(buffer, 0, length)可能会工作,它甚至会很难找到一个测试来打破它。但它只是不保证,它可能会在未来打破。

这里最好的解决办法是使用一个专门的方法从库。在这种情况下

byte[] buffer = System.IO.File.ReadAllBytes(fileName); 

就让我们来看看与反射证实,这将让你的局部缓冲逻辑你流的异常安全Dispose()

而当框架的未来版本允许更好的方法来做到这一点你的代码会自动的利润。