34

Delphi 2010 has a nice set of new file access functions in IOUtils.pas (I especially like the UTC versions of the date-related functions). What I miss so far is something like

TFile.GetSize (const Path : String)

What is the Delphi 2010-way to get the size of a file? Do I have to go back and use FindFirst to access TSearchRec.FindData?

Thanks.

Warren P
  • 65,725
  • 40
  • 181
  • 316
jpfollenius
  • 16,456
  • 10
  • 90
  • 156
  • 2
    Just looked in the JEDI code library at their FileGetSize function. It, in fact, just uses the FindFirst method as well. – J__ Oct 29 '09 at 08:41
  • @J__ why not an answer ? add links to JCL wiki and JCL download - and that would constitute a good catch – Arioch 'The Nov 01 '12 at 06:11
  • Please, vote for http://qc.embarcadero.com/wc/qcmain.aspx?d=110073 – Arioch 'The Jun 01 '13 at 14:49
  • Check it: https://stackoverflow.com/questions/13430927/filesize-what-difference-from-this-procedure-and-what-is-better-use#13431134 – yonni Jul 13 '22 at 16:49

7 Answers7

56

I'm not sure if there's a "Delphi 2010" way, but there is a Windows way that doesn't involve FindFirst and all that jazz.

I threw together this Delphi conversion of that routine (and in the process modified it to handle > 4GB size files, should you need that).

  uses
    WinApi.Windows;

  function FileSize(const aFilename: String): Int64;
  var
    info: TWin32FileAttributeData;
  begin
    result := -1;

    if NOT GetFileAttributesEx(PChar(aFileName), GetFileExInfoStandard, @info) then
      EXIT;

    result := Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32);
  end;

You could actually just use GetFileSize() but this requires a file HANDLE, not just a file name, and similar to the GetCompressedFileSize() suggestion, this requires two variables to call. Both GetFileSize() and GetCompressedFileSize() overload their return value, so testing for success and ensuring a valid result is just that little bit more awkward.

GetFileSizeEx() avoids the nitty gritty of handling > 4GB file sizes and detecting valid results, but also requires a file HANDLE, rather than a name, and (as of Delphi 2009 at least, I haven't checked 2010) isn't declared for you in the VCL anywhere, you would have to provide your own import declaration.

Daniel Marschall
  • 3,739
  • 2
  • 28
  • 67
Deltics
  • 22,162
  • 2
  • 42
  • 70
  • 1
    Smasher didn't explain what kind of file size wonts to get. – GJ. Oct 29 '09 at 09:37
  • 7
    For a compressed file (zip, rar, 7z etc) it will report the size of the archive in bytes, not the size of the decompressed contents. But for a ZIP file, the size of the file IS the size of the archive. For a file on a compressed volume it will report the size of the decompressed file, which is what you want because you are asking for the size of the storage object (the file) not the physical storage medium (the OS enforced compression). For files < 4GB the approach you suggest is quite simply more complicated unless you naively and incorrectly *assume* that all files are always < 4GB – Deltics Nov 01 '09 at 20:09
  • @Deltics about file handle - in XE2 at least (but i believe MUCH before) you can get THandle via [FileOpen](http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.FileOpen) - yet that would require extra variable and `try-finally` to close that handle. – Arioch 'The Nov 01 '12 at 06:14
  • 2
    @Deltics This is NOT correct! This doesn't return sizes greater then 4GB. Please see [how can I get a result larger than 4gb from shl](http://stackoverflow.com/questions/8127693/how-can-i-get-a-result-larger-than-232-from-shl) or use a LARGE_INTEGER. – Roeland Van Heddegem Mar 19 '14 at 15:54
  • 1
    @rvheddeg - Good spot. I fixed the or'd shl with the necessary casts. Might I suggest that a more helpful comment would have been something like "this doesn't work for > 4GB file sizes unless you cast both operands in the final 'or' statement to Int64()" ? :) – Deltics Mar 20 '14 at 20:31
  • @Deltics Of course you might. But I disagree. 1) I don't have the space or the layout in the comments to explain it properly 2) If I point you to the right question you have all the information there (what's wrong with that?) 3) I think you need to cast only the nFileSizeHigh to Int64 (yes, I don't agree completely with the other thread also) 4) Might I suggest that you upvote helpful answers and comments ;-). Have a nice day and thank you for the answer – Roeland Van Heddegem Mar 21 '14 at 08:21
  • 4
    @rvheddeg. Of course you had room. I managed to include all the important information in the "correction", together with thanks,with room to spare. Your unqualified statement that the answer was "NOT correct!" with a link to another question without any indication as to why it was relevant, was not actually very helpful. Not least because the answer fundamentally was correct, albeit with a bug, and crucially now is completely correct, which a reader of your comment would not be able to determine. Otherwise I absolutely *would* have upvoted it. Have a wonderful day. – Deltics Mar 23 '14 at 23:25
  • @Deltics. Maybe we should act as grown-ups and clean up this mess. I have the impression that your major issues are that you lost some time to figure out what was wrong and the "unqualified statement" "NOT correct". I can do nothing about the first issue (note that I lost way more time to find this bug in a large application from an ex-colleague), but if I add a new comment saying that your code has a bug and points an URL to the mentioned article, and remove the rest of my comments including this one, maybe we could come to an agreement? – Roeland Van Heddegem Mar 26 '14 at 13:40
  • 4
    You could also do `Int64Rec(Result).Lo := Info.nFileSizeLow; Int64Rec(Result).Hi := Info.nFileSizeHigh;` – Ondrej Kelle Apr 27 '14 at 10:27
  • Can this code popup critical errorhandlers? Otherwise it should be wrapped in a OldMode := SetErrorMode(SEM_FAILCRITICALERRORS); try ... finally seterrormode(oldmode); end; block – Marco van de Voort Dec 23 '15 at 16:10
  • I used TFileStream.Create to get file size but it failed when the file was locked. Your solution is better! – Gabriel Apr 22 '17 at 12:23
10

Using an Indy unit:

uses IdGlobalProtocols;

function FileSizeByName(const AFilename: TIdFileName): Int64;
Warren P
  • 65,725
  • 40
  • 181
  • 316
tz.
  • 731
  • 7
  • 13
  • returns 0 in 2010 delphi on TXT file. – Vova Popov Mar 03 '13 at 17:53
  • 2
    If `FileSizeByName()` is returning 0, then that is the actual size being reported by the OS, as `FileSizeByName()` returns -1 if an error occurs, like if the file does not exist or cannot be accessed. – Remy Lebeau Mar 20 '14 at 20:59
4

You can also use DSiFileSize from DSiWin32. Works in "all" Delphis. Internally it calls CreateFile and GetFileSize.

function DSiFileSize(const fileName: string): int64;
  var
    fHandle: DWORD;
  begin
    fHandle := CreateFile(PChar(fileName), 0, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if fHandle = INVALID_HANDLE_VALUE then
      Result := -1
    else try
      Int64Rec(Result).Lo := GetFileSize(fHandle, @Int64Rec(Result).Hi);
    finally CloseHandle(fHandle); end;
  end; { DSiFileSize }
Warren P
  • 65,725
  • 40
  • 181
  • 316
gabr
  • 26,580
  • 9
  • 75
  • 141
3

I'd like to mention few Pure Delphi ways. Though i think Deltics made a most speed-effective answer for Windows platform, yet sometimes you want just rely on RTL and also make portable code that would work in Delphi for MacOS or in FreePascal/Virtual Pascal/whatever.


There is FileSize function left from Turbo Pascal days.

The sample above lacks "read-only" mode setting. You would require that to open r/o file such as one on CD-ROM media or in folder with ACLs set to r/o. Before calling ReSet there should be zero assigned to FileMode global var.

It would not work on files above 2GB size (maybe with negative to cardinal cast - up to 4GB) but is "out of the box" one.


There is one more approach, that you may be familiar if you ever did ASM programming for MS-DOS. You Seek file pointer to 1st byte, then to last byte, and check the difference.
I can't say exactly which Delphi version introduced those, but i think it was already in some ancient version like D5 or D7, though that is just common sense and i cannot check it.
That would take you an extra THandle variable and try-finally block to always close the handle after size was obtained.

Aside from 1st approach this is int64-capable. It is also compatible with FreePascal, though with some limitations


You can also create and use TFileStream-typed object - which was the primary, officially blessed avenue for file operations since Delphi 1.0

As a side note, this avenue is of course integrated with aforementioned IOUtils unit.

Arioch 'The
  • 15,799
  • 35
  • 62
  • I'm not sure if TFileStream was sanctioned to be abused for what was officially a directory, not file operation. Findfirst makes more sense as prefered route for this. – Marco van de Voort Feb 26 '17 at 16:16
  • Questionable. I do not want to FIND anything, nor do I want to create and then dispose of file searching infrastructure. I want just to query one specific file. Then, if that file is not alone, and you actually want to collect sizes for a lot of files (directory listing for example), then FindFirst or other directory-oriented approaches would make more sense (as Git developers learned in a bad way :-) ) – Arioch 'The Mar 10 '17 at 17:23
  • You call it finding files, but you can also consider it querying directory services for attributes. And size is a file attribute. The creating/disposing bit also goes for tfilestream object, and, worse, for opening a file just to get the size (the VM system might [reread the whole file). On Windows there is of course GetFileAttributesEx as perfect intermediate, but that concept of getting attributes for one file is not very portable. – Marco van de Voort Mar 10 '17 at 18:08
  • -1 for option a and be being archaic, but +1 for last option because its the modern way and (unlike an option that you didn't mention but was voted high) doesn't involve Windows API, so is platform independant. – Tuncay Göncüoğlu Mar 23 '18 at 12:50
  • It is up to reader to choose the option that best suits his task at hands. What iv the reader would have to read/maintain old code? – Arioch 'The Mar 25 '18 at 19:12
2

This is a short solution using FileSize that does the job:

function GetFileSize(p_sFilePath : string) : Int64;
var
  oFile : file of Byte;
begin
  Result := -1;
  AssignFile(oFile, p_sFilePath);
  try
    Reset(oFile);
    Result := FileSize(oFile);
  finally
    CloseFile(oFile);
  end;
end;

From what I know, FileSize is available only from XE2.

sygi
  • 4,557
  • 2
  • 32
  • 54
LZR
  • 948
  • 10
  • 28
  • 1
    now. I is available even before the Delphi 1.0 was released. http://turbopascal.org/system-functions-filepos-and-filesize I mentioned that function in my list above. Its main problem - except the LOT of boilerplate is that it pukes on files larger than 2GB – Arioch 'The Mar 10 '17 at 17:26
  • it works on D7 too, but there was an error in the parameter name, it means it's not been tested, not even once ;) Problems with files >2Gb but, as said, if that's enough, it works. – ZioBit Apr 16 '17 at 13:54
1
uses
  System.Classes, System.IOUtils;

function GetFileSize(const FileName : string) : Int64;
var
  Reader: TFileStream;
begin
  Reader := TFile.OpenRead(FileName);
  try
    result := Reader.Size;
  finally
    Reader.Free;
  end;
end;
  • @JF: its a 1-liner with one parameter and a try/finally around read/free.. Even tho it would be nice, I don't think much explanation is needed. :-) – Tuncay Göncüoğlu Mar 23 '18 at 14:16
0

There actually is TFile.GetSize in recent Delphi versions: https://docwiki.embarcadero.com/Libraries/Alexandria/ja/System.IOUtils.TFile.GetSize

oxo
  • 946
  • 9
  • 21