Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preallocating file space in C#?

Tags:

c#

file

file-io

I am creating a downloading application and I wish to preallocate room on the harddrive for the files before they are actually downloaded as they could potentially be rather large, and noone likes to see "This drive is full, please delete some files and try again." So, in that light, I wrote this.

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

It works, atleast until you download a file that is several hundred MB's, or potentially even GB's and you throw Windows into a thrashing frenzy if not totally wipe out the pagefile and kill your systems memory altogether. Oops.

So, with a little more enlightenment, I set out with the following algorithm.

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);
}

This works, well even, and doesn't suffer the crippling memory problem of the last solution. It's still slow though, especially on older hardware since it writes out (potentially GB's worth of) data out to the disk.

The question is this: Is there a better way of accomplishing the same thing? Is there a way of telling Windows to create a file of x size and simply allocate the space on the filesystem rather than actually write out a tonne of data. I don't care about initialising the data in the file at all (the protocol I'm using - bittorrent - provides hashes for the files it sends, hence worst case for random uninitialised data is I get a lucky coincidence and part of the file is correct).

like image 884
Matthew Scharley Avatar asked Sep 19 '08 01:09

Matthew Scharley


3 Answers

FileStream.SetLength is the one you want. The syntax:

public override void SetLength(
    long value
)
like image 75
Doug McClean Avatar answered Oct 26 '22 03:10

Doug McClean


If you have to create the file, I think that you can probably do something like this:

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

Where length_to_write would be the size in bytes of the file to write. I'm not sure that I have the C# syntax correct (not on a computer to test), but I've done similar things in C++ in the past and it's worked.

like image 24
Mark Avatar answered Oct 26 '22 01:10

Mark


Unfortunately, you can't really do this just by seeking to the end. That will set the file length to something huge, but may not actually allocate disk blocks for storage. So when you go to write the file, it will still fail.

like image 31
user10392 Avatar answered Oct 26 '22 01:10

user10392