delphi - calculate directory size API?
Asked Answered
E

6

3

Anyone knows other way to get a directoy's size, than calculate it's size by counting file with file? I'm interested on some win32 API function. I've google it about this, but i didn't found relevant information so i'm asking here.

PS: I know how to calculate a directory size by using findfirst and findnext and sum all file's size.

Ehlers answered 22/7, 2010 at 10:8 Comment(2)
It must be API?Valida
@Allforfree - Not necessary.Ehlers
E
4

Getting the size of one directory is a pretty big problem, mostly because it's something you can't define. Examples of problems:

  • Some filesystems, including NTFS and most filesystems on Linux support the notion of "hard links". That is, the very same file may show up in multiple places. Soft links (reparse points) pose the same problem.
  • Windows allows mounting of file systems to directories. Example: "C:\DriveD" might be the same thing as "D:\".
  • Do you want the file size on disk or the actual size of the file?
  • Do you care about actual DIRECTORY entries? They also take up space!
  • What do you do with files you don't have access to? Or directories you don't have permission to browse? Do you count those?

Taking all this into account means the only possible implementation is a recursive implementation. You can write your own or hope you find a ready-written one, but the end performance would be the same.

Escargot answered 22/7, 2010 at 12:0 Comment(2)
1) don't care about those...i'll parse only fadirectories 2) same as 1 3) actual size 4) no, i need the size of files inside the folder 5) will be installed with "super admin" rights I understand. so the only solution is to use my own.... ok, thanks everybody best regards,Ehlers
+1 for the answer. blogs.msdn.com/b/oldnewthing/archive/2004/12/28/336219.aspxStood
R
3

I don't think there's an API for this. I don't think Windows knows the answer. i.e. it would have to recursively search the tree and summarize each folder, using the technique that Marcelo indicated.
If there were such an API call available, then other things would use it too and Windows would be able to immediately tell you folder sizes.

Rye answered 22/7, 2010 at 11:57 Comment(0)
V
2

I already had a function to list files of a folder (and subfolders) and one to read the file size. So, I only wrote a small procedure that puts these two together.

ListFilesOf

function ListFilesOf(CONST aFolder, FileType: string; CONST ReturnFullPath, DigSubdirectories: Boolean): TTSL;
{ If DigSubdirectories is false, it will return only the top level files,
  else it will return also the files in subdirectories of subdirectories.
  If FullPath is true the returned files will have full path.
  FileType can be something like '*.*' or '*.exe;*.bin'
  Will show also the Hidden/System files.
  Source Marco Cantu Delphi 2010 HandBook

   // Works with UNC paths}
VAR
  i: Integer;
  s: string;
  SubFolders, filesList: TStringDynArray;
  MaskArray: TStringDynArray;
  Predicate: TDirectory.TFilterPredicate;

 procedure ListFiles(CONST aFolder: string);
 VAR strFile: string;
 begin
  Predicate:=
        function(const Path: string; const SearchRec: TSearchRec): Boolean
        VAR Mask: string;
        begin
          for Mask in MaskArray DO
            if System.Masks.MatchesMask(SearchRec.Name, Mask)
            then EXIT(TRUE);
          EXIT(FALSE);
        end;

  filesList:= TDirectory.GetFiles (aFolder, Predicate);
  for strFile in filesList DO
   if strFile<> ''                                                                                 { Bug undeva: imi intoarce doua intrari empty ('') }
   then Result.Add(strFile);
 end;

begin
 { I need this in order to prevent the EPathTooLongException (reported by some users) }
 if aFolder.Length >= MAXPATH then
  begin
   MesajError('Path is longer than '+ IntToStr(MAXPATH)+ ' characters!');
   EXIT(NIL);
  end;

 if NOT System.IOUtils.TDirectory.Exists (aFolder)
 then RAISE Exception.Create('Folder does not exist! '+ CRLF+ aFolder);

 Result:= TTSL.Create;

 { Split FileType in subcomponents }
 MaskArray:= System.StrUtils.SplitString(FileType, ';');

 { Search the parent folder }
 ListFiles(aFolder);

 { Search in all subfolders }
 if DigSubdirectories then
  begin
   SubFolders:= TDirectory.GetDirectories(aFolder, TSearchOption.soAllDirectories, NIL);
   for s in SubFolders DO
     if cIO.DirectoryExists(s)                                                                     { This solves the problem caused by broken 'Symbolic Link' folders }
     then ListFiles(s);
  end;

 { Remove full path }
 if NOT ReturnFullPath then
  for i:= 0 to Result.Count-1 DO
   Result[i]:= TPath.GetFileName(Result[i]);
end;

GetFileSize

{ Works with >4GB files
  Source: https://mcmap.net/q/434952/-getting-size-of-a-file-in-delphi-2010-or-later }
function GetFileSize(const aFilename: String): Int64;
VAR
   info: TWin32FileAttributeData;
begin
 if GetFileAttributesEx(PWideChar(aFileName), GetFileExInfoStandard, @info)
 then Result:= Int64(info.nFileSizeLow) or Int64(info.nFileSizeHigh shl 32)
 else Result:= -1;
end;

Finally:

function GetFolderSize(aFolder: string; FileType: string= '*.*'; DigSubdirectories: Boolean= TRUE): Int64;
VAR
   i: Integer;
   TSL: TTSL;
begin
 Result:= 0;
 TSL:= ListFilesOf(aFolder, FileType, TRUE, DigSubdirectories);
 TRY
  for i:= 0 to TSL.Count-1 DO
   Result:= Result+ GetFileSize(TSL[i]);
 FINALLY
  FreeAndNil(TSL);
 END;
end;

Note that:
1. You can only count the size of some file types in a folder. For example in a folder containing BMP and JPEG files, if you want/need, you can only obtain the folder size only for BMP files.
2. Multiple filetypes are supported, like this: '.bmp;.png'.
3. You can choose if you want to read or not rea the size of the sub-folders.

Further improvements: You can massively reduce the size of the code by eliminating the GetFolderSize and moving the GetFileSize directly into ListFilesOf.

Guaranteed to work on Delphi XE7.

Valida answered 10/8, 2017 at 7:32 Comment(1)
Thank you for the answer, event it's coming after 7 years :) Original question was for D7. Please leave the answer here, so others could use it if applicable. +1Ehlers
J
1

If you already know how to do another level, just use recursion to get every level. As your function traverses the current directory, if it comes across subdirectory, call the function recursively to get the size of that subdirectory.

Jerlenejermain answered 22/7, 2010 at 10:15 Comment(1)
thank you, but i've already said that i need some api function if exist.Ehlers
N
1

You can use FindFile component. It will recursive the directories for you.

http://www.delphiarea.com/products/delphi-components/findfile/

Nonoccurrence answered 22/7, 2010 at 22:12 Comment(0)
C
-1

Many of the previous examples of recurring find first and find next implementations do not consider the larger file capacities. Those examples do not produce correct results. A recursion routine that accommodates larger file capacities is available.

{*-----------------------------------------------------------------------------
 Procedure: GetDirSize
 Author:    archman
 Date:      21-May-2015
 @Param     dir: string; subdir: Boolean
 @Return    Int64
 -----------------------------------------------------------------------------}

function TBCSDirSizeC.GetDirSize(dir: string; subdir: Boolean): Int64;
var
  rec: TSearchRec;
  found: Integer;
begin
  Result := 0;
  if dir[Length(dir)] <> '\' then
    dir := dir + '\';
  found := FindFirst(dir + '*.*', faAnyFile, rec);
  while found = 0 do
  begin
    Inc(Result, rec.Size);
    if (rec.Attr and faDirectory > 0) and (rec.Name[1] <> '.') and
      (subdir = True) then
      Inc(Result, GetDirSize(dir + rec.Name, True));
    found := FindNext(rec);
  end;
  System.SysUtils.FindClose(rec);
end;

Please visit the link below for a complete explanation of the process. http://bcsjava.com/blg/wordpress/2015/06/05/bcs-directory-size-delphi-xe8/

Citriculture answered 18/8, 2016 at 13:18 Comment(1)
As you might see here rec.Size sometimes negative decreasing the Result: i.sstatic.net/dxh5g.pngBeabeach

© 2022 - 2024 — McMap. All rights reserved.