在C#中缓存二进制文件

7

在.NET中,是否可以对二进制文件进行缓存,并对缓存的文件进行常规文件操作?


2
哎?你指的是什么意思,1)缓存?2)二进制文件(例如非文本文件、可执行文件、图像)?3)“普通文件”操作? - Binary Worrier
1
还有,为什么你想要缓存它?也许这是不必要的? - uriDium
请提供使用案例。 - Preet Sangha
我有一个二进制文件,在其中进行二分查找,并且在我的网页上经常访问它。我的想法是为了快速访问而将其缓存以供搜索。 - nLL
我已经搜索了一个小时了,为什么没有人回答问题,而是假设有一个聪明的操作系统和开发者正在本地访问文件?我想知道。如果您不知道如何高效地缓存文件,无论操作系统或文件大小如何,就没有必要建议使用内存流。/抱怨抱歉,不仅是你。我看到了其他几个问题/答案,都涉及到相同的MemoryStream内容,这让我发疯了。 - Behrooz
5个回答

14

这种方法是先将FileStream中的所有内容读入到一个MemoryStream对象中,然后再将这个对象用于后续的I/O操作。两种类型都继承自Stream,因此使用方式基本相同。

以下是示例代码:

private MemoryStream cachedStream;

public void CacheFile(string fileName)
{
    cachedStream = new MemoryStream(File.ReadAllBytes(fileName));
}

当你希望缓存给定的文件时,只需调用CacheFile方法一次,然后在代码的任何其他地方使用cachedStream进行读取。(实际文件将在其内容被缓存后立即关闭。)唯一需要记住的是在完成后处理cachedStream


这可能没问题 - 唯一的问题是,如果我们谈论的是一个大小为 GB 或两个 GB 的文件。 - Daniel Earwicker
2
是的,当文件大小接近 RAM 大小时,该方法肯定会失去用处。那时候,您应该使用数据库服务器,所以我认为这不会成为一个问题。 - Noldorin

4
任何现代操作系统都有内置的缓存系统,因此实际上每当您与文件交互时,您都是在与文件的内存缓存交互。
在应用自定义缓存之前,您需要问一个重要的问题:当基础文件更改时,我的缓存副本变得无效会发生什么?
如果允许缓存副本更改,并且需要将更改保存回基础文件,则可以进一步复杂化问题。
如果文件很小,只需像另一个答案中建议的那样使用MemoryStream即可。
如果需要将更改保存回文件,则可以编写一个包装类,将所有内容转发到MemoryStream,但还具有IsDirty属性,每当执行写操作时它就将其设置为true。然后,您可以编写一些管理代码,在您选择的时间(在某个较大事务的末尾?)检查(IsDirty == true),并将新版本保存到磁盘。这称为“惰性写入”缓存,因为修改是在内存中进行的,直到稍后才实际保存。
如果您真的想要使事情变得复杂,或者您有一个非常大的文件,您可以实现自己的分页,选择一个缓冲区大小(也许是1 MB?),并保持小量固定大小的byte []页面。这一次,您将为每个页面设置一个脏标志。您将实现Stream方法,使其隐藏调用方的细节,并在必要时拉入(或丢弃)页面缓冲区。
最后,如果您想要更轻松的生活,请尝试:

http://www.microsoft.com/Sqlserver/2005/en/us/compact.aspx

它让你使用与SQL Server相同的SQL引擎,但在文件上运行,所有操作都在你的进程内部完成,而不是通过外部RDBMS服务器。这可能会为您提供更简单的查询和更新文件的方式,并避免大量手写持久化代码的需要。

这不就是内存映射文件吗?即便如此,我认为 OP 想尽快关闭文件句柄。 - Noldorin
内存映射文件是指操作系统使用一个文件(由您选择)为进程地址空间的一部分提供虚拟内存后备存储。 (页面文件用于通常分配内存的此目的。)我说的是操作系统具有磁盘缓存,无论您如何访问文件都会运行。 尝试使用grep或类似工具搜索几百MB的文本文件。 第二次执行时,速度会更快,硬盘不会发出声音,因为所有内容都在内存中。 - Daniel Earwicker
@Earwicker:是的,我相信你是对的。尽管如此,将内容复制到MemoryStream中似乎是最好的解决方案,因为a)它不会在文件上保持锁定,b)我认为它仍然会提供性能增益。 - Noldorin

3

当然,你可以将文件读入一个byte[]数组并开始处理。如果您想使用流,则可以将FileStream复制到MemoryStream中并开始使用它,例如:

public static void CopyStream( Stream input, Stream output )
{
        var buffer = new byte[32768];
        int readBytes;
        while( ( readBytes = input.Read( buffer, 0, buffer.Length ) ) > 0 )
        {
                output.Write( buffer, 0, readBytes );
        }
}

如果你关心性能——通常来说,不同的文件访问方法内置的机制应该足够。


0
我不知道你具体在做什么,但我提供这个建议(根据你的实际情况可能可行或不可行):
不要只缓存文件内容,为什么不将文件内容放入一个漂亮的强类型项目集合中,然后进行缓存?这样做可能会使查找项目变得更容易、更快,因为没有解析过程。

文件包含很多记录。实际上,它是MaxMind国家数据库的二进制文件。 - nLL
从中我们可以推断出,真正的问题是您无法从查询中获得所需的性能吗? - Sam Holder

0

Lucene中有一个非常优雅的缓存系统,它将字节从磁盘缓存到内存中,并智能更新存储等。你可能想看看那些代码,以了解他们如何实现。你也可以阅读 Microsoft SQL Server 数据存储层的相关资料 - 因为MSSQL团队对一些更关键的实现细节非常乐于分享。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接