Size On Disk is the actual size that your files occupy on the drive depending on the cluster size (or allocation unit), which most of the time is 4KB, but not all of the time. It depends on the file format and how it was formatted.
As long as the files are not compressed, it's a matter of finding out how many chunks of clusters are needed to fit each file. Keeping in mind that if a file is smaller than the cluster size, it will occupy one allocation unit.
If the file is compressed, the information is not easily available and needs to be retrieved via an API.
The following code is divided in 3 main sections:
- It defines a Type used to access the function
GetCompressedFileSizeAPI
in kernel.dll. This function will retrieve the compressed size on disk of the file.
- It uses WMI to determine the cluster size for the given
$path
- It computes the Size on Disk for the files in the folder and subfolders of
$path
depending if the file is compressed or not.
Note that the call to Get-ChildItem
uses the -force
switch to ensure we retrieve hidden and system files as well.
I'm not sure if this code will work with Skydrive, so you might need to change the WMI part.
$path = '.\'
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
public class FileInfo
{
[DllImport("kernel32.dll", SetLastError=true, EntryPoint="GetCompressedFileSize")]
static extern uint GetCompressedFileSizeAPI(string lpFileName, out uint lpFileSizeHigh);
public static ulong GetCompressedFileSize(string strFileName)
{
uint intHigh;
uint intLow;
intLow = GetCompressedFileSizeAPI(strFileName, out intHigh);
int intError = Marshal.GetLastWin32Error();
if (intHigh == 0 && intLow == 0xFFFFFFFF && intError != 0)
throw new Win32Exception(intError);
else
return ((ulong)intHigh << 32) + intLow;
}
}
"@
$files = Get-ChildItem $path -Recurse -force | where {$_.PSIsContainer -eq $false}
$drive = [string]$files[0].PSdrive+':'
$wql = "SELECT Blocksize FROM Win32_Volume where DriveLetter='$drive'"
$driveinfo = Get-WmiObject -Query $wql -ComputerName '.'
$sizeondisk = ($files | %{
if ($_.Attributes -like "*compressed*")
{
if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
{
$driveinfo.BlockSize
}
else
{
[FileInfo]::GetCompressedFileSize($_.fullname)
}
}
else
{
if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
{
$driveinfo.BlockSize
}
else
{
([math]::ceiling($_.length/$driveinfo.BlockSize))*$driveinfo.BlockSize
}
}
}|Measure -sum).sum
$sizeondisk
UPDATE for Sparse Files:
Let's see if this version works with sparse files, add this block of code at the end of the previous code, keep everything else the same:
$sparsesize = ($files | %{
if ($_.length -lt $driveinfo.BlockSize -and $_.length -ne 0)
{
$driveinfo.BlockSize
}
else
{
$_.fullname
[FileInfo]::GetCompressedFileSize($_.fullname)
}
}|Measure -sum).sum
$sparsesize
Sources:
Understanding NTFS compression
Help querying files to return SIZE on DISK
Size of compressed files (in French)
How to get the actual size-on-disk of a file from PowerShell?
Default cluster size for NTFS, FAT, and exFAT