在C#中秒级创建一个巨大的虚拟文件

42

我想在几秒钟内创建一个很大的虚拟文件,比如1~2 GB。 这是我用C#写的:

file.writeallbytes("filename",new byte[a huge number]);

另一种指示状态的方法如下:

long FSS = din.TotalFreeSpace;
long segments = FSS / 10000;
long last_seg = FSS % 10000;
BinaryWriter br = new BinaryWriter(fs);

for (long i = 0; i < segments; i++)
{
    br.Write(new byte[10000]);

    this.label2.Text = "segments write :" + i.ToString() + "\r\n" + "segments remain :" + ((segments-i)+1).ToString();
    Application.DoEvents();
}
br.Write(new byte[last_seg]);
this.label2.Text += "\r\nDone!";
br.Close();

其中din是磁盘信息对象

使用这两种方法编写如此庞大但无用的文件大约需要2分钟或更长时间。是否有其他更快的方式来完成此操作?


3
BinaryWriter是用于将POCO写入文件的一种格式,以便.NET可以从中读取,这与名称所暗示的不同。 - Chris S
5个回答

92

创建文件后,将其定位到足够大的偏移量,并写入一个字节即可:

FileStream fs = new FileStream(@"c:\tmp\huge_dummy_file", FileMode.CreateNew);
fs.Seek(2048L * 1024 * 1024, SeekOrigin.Begin);
fs.WriteByte(0);
fs.Close();

这将产生一个大小为2GB的文件,基本上内容是不可预测的,但对于您的目的来说应该没问题。


3
一个有趣的简单解决方案(假设使用虚拟内容是可以的),点赞加一。 - Abel
8
写入时文件内容不可预测,将填充零以确保安全性。这一步骤是必要的。 - RickNZ
2
@RickNZ:虽然这对于常见的文件系统确实是如此,但它并不是API合同的显式部分,并且对于某些第三方网络重定向器来说也不是这种情况。 - mdb
请注意,以这种方式生成的文件会导致Firefox文件上传崩溃。不知道为什么。 - Teoman shipahi
4
了解,这将生成一个大小为2 GiB + 1字节的文件。 - PythonJin
显示剩余3条评论

76

如果您不在意内容,那么我所知道的最快的方法是这个——它实际上是瞬间完成的:

private void CreateDummyFile(string fileName, long length)
{
    using (var fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None))
    {
        fileStream.SetLength(length);
    }
}

12
出于好奇心,测试了 SetLengthSeek + WriteByte 方法。使用 Seek 方法,512MB 文件需要 15 秒左右,而只使用 SetLength 方法则只需约 1 秒钟。在两种情况下,即使进行了多次测试,文件中也都被填充了 NULL(而不是任意数据)。不确定这是一个干净的磁盘还是某种优化的零填充创建方式。 - Abel
不错的发现 - 非常有趣。我可以确认我的情况也是一样的;我敢猜测在我的情况下很可能不是干净的硬盘(混乱和满硬盘!) - Rob Levine
对我来说,这比mdb的方法花费更长时间 :-? - Vahid Hashemi
你尝试过多次对其进行基准测试,每次测试后删除文件,以排除磁盘填充或其他因素可能产生的影响吗? - Rob Levine
用秒表计时 :D 所有三种方法(我的+您的+mdbs)都在相同的范围内。没有显着的差异 :-? 正如我所提到的,我需要这个用于可移动存储,我所有的测试都是在可移动存储上进行的。 - Vahid Hashemi
这是MSDN关于SetLength()的说法:“如果流被扩展,则旧长度和新长度之间的流内容是未定义的。” https://msdn.microsoft.com/zh-cn/library/system.io.filestream.setlength(v=vs.110).aspx - Vincent

4
如果你只需要一个FileStream,你可以使用FileStream.SetLength。这将使你获得一个长度为2 GB的流。然后你可以在任意位置写入最后一个字节。但是内容将是未定义的。
如果你想在磁盘上实际创建一个文件,那么是的,你需要实际写入它的内容。而且是的,硬盘会很慢;像1 GB/分钟的写入速度并不算完全荒谬。抱歉——这是物理学!

2
为什么你没有使用BackgroundWorker类来实现这个,因为你可以将任何东西传递到方法ReportProgress中来指示状态报告。请参见以下示例:
        private BackgroundWorker bgWorker;
        public Form1()
        {
            InitializeComponent();
            bgWorker = new BackgroundWorker();
            bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
            bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
            bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
            bgWorker.RunWorkerAsync();
        }
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.label2.Text = "完成"; }
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { MyStatus myProgressStatus = (MyStatus)e.UserState; this.label2.Text = string.Format("已写入片段: {0}" + Environment.Newline + "剩余片段: {1}", myProgressStatus.iWritten, myProgressStatus.iRemaining); }
void bgWorker_DoWork(object sender, DoWorkEventArgs e) { long FSS = din.TotalFreeSpace; long segments = FSS / 10000; long last_seg = FSS % 10000; BinaryWriter br = new BinaryWriter(fs);
for (long i = 0; i < segments; i++) { br.Write(new byte[10000]); bgWorker.ReportProgress(i.ToString(), new MyStatus(i, ((segments-i) + 1)));
} br.Write(new byte[last_seg]); br.Close(); } public class MyStatus{ public int iWritten; public int iRemaining; public MyStatus(int iWrit, int iRem){ this.iWritten = iWrit; this.iRemaining = iRem; } } }
这是一个草稿...

0

我可能错了,但你可能会发现创建一个如此大的文件是不可能那么快的,因为在I/O写入过程中会有瓶颈。

然而,在你上面的代码中,Applciation.DoEvents会减慢速度。此外,任何重新绘制screenthis.label2.Text =都会导致轻微的减速。


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