PowerShell to get Size on Disk of files

Updated on 10th February 2017

If you right click a file or folder in windows and look at it properties you will find there are two size properties - Size and Size on Disk. Size is the actual size of the file in bytes and Size on disk is the disk space required to store that file. The two sizes can be the same in some cases or it could be hugely different especially if its a sparse file or if its a compressed file that is stored in a volume that supports compression.

To get the size of a file in PowerShell you just have to run the Get-Item command

PS C:\Users\> Get-Item export.sql

    Directory: C:\Users

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        23/01/2017     15:25     177331 export.sql

p>To retrieve the "Size on Disk" of a file there isn't any command or cmdlets, however we could achieve this with .Net Platform Invoke (P/Invoke) feature and the PowerShell Add-Type cmdlet.

First you create a .NET class that finds the actual size on disk of a file. Inside this class you will use the Win32 function GetCompressedFileSizeW to which returns the size in bytes used to store a file.

The Add-Type cmdlet is used to add the class to your PowerShell session. Finally get each file from the specfied directory and call the function in .Net class you created with full file name as paramater.

Here is the full script:

Script: Get-FileSizeOnDisk.ps1


<#

.SYNOPSIS
  Powershell script to get file size and size on disk of all files
  in a directory.
  
.DESCRIPTION
  This PowerShell script gets file size and size on disk in bytes
  of all files in a directory.
  
.PARAMETER <path>
   Directory path of the files to check. If this parameter is not
   specified the default value is current directory.
 
.NOTES
  Version:        1.0
  Author:         Open Tech Guides
  Creation Date:  06-Feb-2017
 
.LINK
    www.opentechguides.com
    
.EXAMPLE
  Get-FileSizeOnDisk c:\myfolder

#>
   

param (
 [string]$path='.'
)


$source = @"
 using System;
 using System.Runtime.InteropServices;
 using System.ComponentModel;
 using System.IO;

 namespace Win32
  {
    
    public class Disk {
	
    [DllImport("kernel32.dll")]
    static extern uint GetCompressedFileSizeW([In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
    [Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh);	
        
    public static ulong GetSizeOnDisk(string filename)
    {
      uint HighOrderSize;
      uint LowOrderSize;
      ulong size;

      FileInfo file = new FileInfo(filename);
      LowOrderSize = GetCompressedFileSizeW(file.FullName, out HighOrderSize);

      if (HighOrderSize == 0 && LowOrderSize == 0xffffffff)
       {
	 throw new Win32Exception(Marshal.GetLastWin32Error());
      }
      else { 
	 size = ((ulong)HighOrderSize << 32) + LowOrderSize;
	 return size;
       }
    }
  }
}

"@

Add-Type -TypeDefinition $source

$result = @()

Get-ChildItem $path | Where-Object { ! $_.PSIsContainer} | Foreach-Object { 
  
    $size = [Win32.Disk]::GetSizeOnDisk($_.FullName)
    $obj = New-Object PSObject
    Add-Member -InputObject $obj -MemberType NoteProperty -Name "File Name" -Value $_.Name
    Add-Member -InputObject $obj -MemberType NoteProperty -Name "Size" -Value $_.Length
    Add-Member -InputObject $obj -MemberType NoteProperty -Name "Size on Disk" -Value $size
    $result += $obj
}

write-output $result 

Sample output

PS C:\> Get-FileSizeOnDisk.ps1 C:\Users

File Name          Size        Size on Disk
---------          ----        ------------
export.sql       177331               45056

PS C:\>