Search code examples
powershellfile-watcher

PowerShell File Watcher Register-ObjectEvent - Wait For File To Complete Copying


The below code is checking for new files in a specified directory $folderCompleted.

Currently, when a small file is placed into that directory (~1MB) the Move-Item command and other file reading checks complete successfully.

However when a large file is moved into this directory, the Object event is called before the file is completely moved (copied over) into that directory. This causing the file checks and the Move-Item command to fail since the file is still in use.

# File Watcher
$filter = '*.*'
$fsw = New-Object IO.FileSystemWatcher $folderCompleted, $filter -Property @{
    IncludeSubdirectories = $true
    NotifyFilter = [IO.NotifyFilters]'FileName, LastWrite'
}

$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
    $path = $Event.SourceEventArgs.FullPath
    $name = $Event.SourceEventArgs.Name
    $changeType = $Event.SourceEventArgs.ChangeType
    $timeStamp = $Event.TimeGenerated
    Write-Host "The file '$name' was $changeType at $timeStamp"

    # File Checks

    # Move File
    Move-Item $path -Destination $destinationPath -Verbose
}

How can I place a check to see if the file is not still being copied over?


Solution

  • Solved this shortly after posting my comment above. Other answers may be similar but ended up adding this function towards the top of the script.

    # function Test-FileLock 
    function Test-FileLock {
      param ([parameter(Mandatory=$true)][string]$Path)
    
      $oFile = New-Object System.IO.FileInfo $Path
    
      if ((Test-Path -Path $Path) -eq $false)
      {
        return $false
      }
    
      try
      {
          $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
          if ($oStream)
          {
            $oStream.Close()
          }
          $false
      }
      catch
      {
        # file is locked by a process.
        return $true
      }
    }
    

    Added this code after the variables section in the $onCreated section.

    # Test File Lock
    Do {
        $filetest = Test-FileLock -Path $path
        sleep -Seconds 5
    } While ($filetest -eq $true)