delphi - calculate directory size API?

最后都变了- 提交于 2020-01-02 07:13:03

问题


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.


回答1:


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.




回答2:


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.




回答3:


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: http://stackoverflow.com/questions/1642220/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.




回答4:


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.




回答5:


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

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




回答6:


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/



来源:https://stackoverflow.com/questions/3307722/delphi-calculate-directory-size-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!