2006. május 19., péntek

How to find the size of a given directory


Problem/Question/Abstract:

I'm looking for a way to find the size of any given directory and all the files in that directory. I know when you look at the properties of a given directory you get the size, date, number of sub- directories, etc. That is the information that I'm looking for.

Answer:

Solve 1:

unit DirectorySize;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TFrmDirectorySize = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    CbxFilesToo: TCheckBox;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  public
  end;

var
  FrmDirectorySize: TFrmDirectorySize;

implementation

{$R *.DFM}

uses
  FileCtrl;

function FormatNumber(ANumber: double): string;
var
  p: integer;
begin
  Result := Format('%n', [ANumber]);
  p := Pos(DecimalSeparator, Result);
  if p > 0 then
    Result := Copy(Result, 1, p - 1);
end;

function GetDirectorySize(APath: string; AList: TStrings; AFilesToo: boolean):
  longint;
const
  indent: string = '';
var
  curItem: integer;
  dirSize: longint;
  kb: double;
  searchRec: SysUtils.TSearchRec;
  tmpS: string;
begin
  if AFilesToo and (indent <> EmptyStr) then
    AList.Add(EmptyStr);
  curItem := AList.Add(indent + APath);
  Result := 0;
  if APath[Length(APath)] <> '\' then
    APath := APath + '\';
  if FindFirst(APath + '*.*', faDirectory, searchRec) = 0 then
    repeat
      {Assure that it's a normal file}
      with searchRec do
      begin
        if Name[1] <> '.' then
          if (Attr and faDirectory > 0) then
          begin
            indent := indent + '  ';
            dirSize := GetDirectorySize(APath + Name + '\', AList, AFilesToo);
            Delete(indent, 1, 2);
            Result := Result + dirSize;
          end
          else
          begin
            Result := Result + searchRec.Size;
            if AFilesToo then
            begin
              {Memo2.Clear; Memo2.Lines.Add(Name);}
              AList.Add(indent + '  ' + Name + ' [' + FormatNumber(searchRec.Size) +
                ']');
            end;
          end;
      end;
      Application.ProcessMessages;
    until FindNext(searchRec) <> 0;
  SysUtils.FindClose(searchRec);
  kb := Result / 1024.0;
  tmpS := ' (' + Format('%nk', [kb]) + ')';
  AList[curItem] := AList[curItem] + ' [' + FormatNumber(Result) + tmpS + ']';
end;

procedure TFrmDirectorySize.Button1Click(Sender: TObject);
var
  directory: string;
begin
  Memo1.Clear;
  Button1.Enabled := false;
  Cursor := crHourglass;
  Memo1.Lines.BeginUpdate;
  try
    Application.ProcessMessages;
    if SelectDirectory('Select A Directory', '', directory) then
    begin
      Caption := Directory;
      GetDirectorySize(directory, Memo1.Lines, CbxFilesToo.Checked);
    end;
  finally
    Cursor := crDefault;
    Button1.Enabled := true;
    Memo1.Lines.EndUpdate;
  end;
end;

procedure TFrmDirectorySize.FormCreate(Sender: TObject);
begin
  Memo2.Clear;
  Icon.Assign(Application.Icon);
end;

end.


Solve 2:

uses
  Windows

function CalcFolderSize(aRootPath: string): Int64;

  procedure Traverse(const aFolder: string);
  var
    Data: TWin32FindData;
    FileHandle: THandle;
  begin
    FileHandle := FindFirstFile(PCHAR(aFolder + '*'), Data);
    if FileHandle <> INVALID_HANDLE_VALUE then
    try
      repeat
        if (Data.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY > 0)
          and (Data.cFileName[0] <> '.') then
          Traverse(aFolder + Data.cFilename + '\')
        else
          Inc(Result, (Data.nFileSizeHigh * MAXDWORD) + Data.nFileSizeLow);
      until
        not FindNextFile(FileHandle, Data);
    finally
      Windows.FindClose(FileHandle);
    end;
  end;

begin
  Result := 0;
  if aRootPath[Length(aRootPath)] <> '\' then
    aRootPath := aRootPath + '\';
  Traverse(aRootPath);
end;


Solve 3:

Most the examples of this I've seen return a 4-byte integer. That's mostly ok for file sizes, but a sum of file size can more easily exceed the 4Gb limit. This example uses and 8-byte (signed) integer:

function DirectorySize(const sPath: TFileName): Int64;
var
  rFind: TSearchRec;
  iSize: Int64;
begin
  Result := 0;
  if SysUtils.FindFirst(IncludeTrailingBackslash(sPath) + '*', faAnyFile, rFind) = 0
    then
  begin
    try
      repeat
        if rFind.Name <> StringOfChar('.', Length(rFind.Name)) then
        begin
          if (rFind.Attr and faDirectory) = faDirectory then
            Result := Result + DirectorySize(IncludeTrailingBackslash(sPath) +
              rFind.Name)
          else
          begin
            iSize := (Int64(rFind.FindData.nFileSizeHigh)shl32) or
              rFind.FindData.nFileSizeLow;
            Result := Result + iSize;
          end;
        end;
      until
        SysUtils.FindNext(rFind) <> 0;
    finally
      SysUtils.FindClose(rFind);
    end;
  end;
end;

Nincsenek megjegyzések:

Megjegyzés küldése