在C#中预分配文件空间?

时间:2020-03-06 14:24:21  来源:igfitidea点击:

我正在创建一个下载应用程序,我希望在实际下载文件之前在硬盘驱动器上为其分配空间,因为它们可能很大,而且没人喜欢看到"此驱动器已满,请删除一些文件,然后重试。 "因此,鉴于此,我写了这篇。

// Quick, and very dirty
System.IO.File.WriteAllBytes(filename, new byte[f.Length]);

至少在下载一个数百MB甚至GB的文件之前,它会一直起作用,并且如果Windows无法彻底擦除页面文件并完全杀死系统内存,则会使Windows陷入混乱的狂潮。哎呀。

因此,有了更多的启发,我开始使用以下算法。

using (FileStream outFile = System.IO.File.Create(filename))
{
    // 4194304 = 4MB; loops from 1 block in so that we leave the loop one 
    // block short
    byte[] buff = new byte[4194304];
    for (int i = buff.Length; i < f.Length; i += buff.Length)
    {
        outFile.Write(buff, 0, buff.Length);
    }
    outFile.Write(buff, 0, f.Length % buff.Length);
}

这甚至可以正常工作,并且不会遭受最后一个解决方案严重的内存问题。但是,它仍然很慢,尤其是在较旧的硬件上,因为它会将数据写出(可能相当于GB的数据)到磁盘上。

问题是:是否有更好的方法完成同一件事?有没有一种方法可以告诉Windows创建一个x大小的文件,并简单地在文件系统上分配空间,而不是实际写出一吨数据。我根本不关心初始化文件中的数据(我正在使用bittorrent的协议会为其发送的文件提供哈希,因此对于随机未初始化的数据,最坏的情况是我碰巧遇到了幸运的情况,并且文件的一部分正确)。

解决方案

如果必须创建文件,我认为我们可能可以执行以下操作:

using (FileStream outFile = System.IO.File.Create(filename))
{
    outFile.Seek(<length_to_write>-1, SeekOrigin.Begin);
    OutFile.WriteByte(0);
}

其中length_to_write将是要写入的文件的大小(以字节为单位)。我不确定我的Csyntax是否正确(不在计算机上进行测试),但是过去我在C ++中做过类似的事情,并且可以正常工作。

FileStream.SetLength是我们想要的那个。语法:

public override void SetLength(
    long value
)

不幸的是,我们不能仅仅通过寻求最终结果来做到这一点。这会将文件长度设置得很大,但实际上可能不会分配磁盘块进行存储。因此,当我们去写文件时,它仍然会失败。