我见过以下代码,用于将文件转换为数组,然后将其用作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,长度),以便只将文件内容复制到缓冲区?
由于
答案 0 :(得分:4)
除了“文件可能不适合内存”之外,还有更多内容。 Stream.Read
的合同明确说:
此方法的实现读取a 来自的最大计数字节数 当前流并存储它们 缓冲区从偏移开始。该 流中的当前位置是 按读取的字节数提前; 但是,如果发生异常,则 流中的当前位置 保持不变。实现 返回读取的字节数。该 只有当返回值为零时才返回零 位置目前正在结束 流。实施将 阻止至少一个字节的数据 如果没有数据,可以读取 是可用的。读取时仅返回0 流中没有更多数据 并且不再期待(例如a 关闭套接字或文件结尾)。一个 实施可以免费返回更少 字节比请求即使结束 尚未到达溪流。
请注意最后一句话 - 您不能依赖对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 :(得分:3)
因为您的文件可能非常大,缓冲区通常具有4-32 KB的固定大小。通过这种方式,你知道你不会忘记你的记忆。
当然,如果您知道文件的大小不是太大或者无论如何都将内容存储在内存中,那么没有理由不一次全部阅读。
虽然,如果您想直接将文件内容读入变量,则不需要Stream API。而是使用
File.ReadAllText(...)
或
File.ReadAllBytes(...)
答案 2 :(得分:1)
一个简单的fs.Read(buffer, 0, length)
可能会起作用,甚至很难找到打破它的测试。但它只是无法保证,并且可能会在未来中断。
这里最好的答案是使用库中的专门方法。在这种情况下
byte[] buffer = System.IO.File.ReadAllBytes(fileName);
使用Reflector快速查看确认这将为您提供部分缓冲逻辑和您的流的异常安全Dispose()
。
当框架的未来版本允许更好的方法来执行此操作时,您的代码将自动获利。