Reputation: 31
I'm still a bit of a newbie at powershell (coming from vbscript) and it has been a while since I have done anything too serious, so I apologise in advance if the code isn't as graceful as it could be.
Scenario is this:
A system we use creates folders in a directory every half an hour, within those subfolders image files can be created (they are distinct names within the subfolder, but are the same convention across the subfolders, so SubFolderA may contain picture1.jpg, picture2.jpg, picture3.jpg, but so may the other subfolders.
I need to monitor the entire tree, and any time a JPG file is created, copy it out, rename it based on date/time/seconds and place it in a 'flat' folder.
In the code below, the behaviour works as expected on the first folder (and I can spawn multiple files and it will copy them all), but when a file appears in the next folder, it says it is running the copy, but then hangs. I don't get any errors/feedback from the script.
The script halts on the 'I'm running this copy', but nothing else happens, if I restart the script it functions fine until changes appear in a 2nd folder. It can be any folder where the first changes occur, any folder after that appears to fail.
I feel like I am doing something wrong in handling the behaviour of the event, but I don't understand it enough to make sense of it. The code itself seems to function fine if changes happen in one folder only.
Directory Tree looks like:
X:\PicsSource
X:\PicsSource\Folder 1\
X:\PicsSource\Folder 2\
X:\PicsSource\Folder 3\
I should also mention that the source will be a network share and the destination will be local initially but may also be a network share in the future. For my testing, both source and destination are network shares (on the same server), with the script executed on another machine.
Within each folder are many files, but I'm only interested in JPG.
Code I have so far:
Unregister-Event -SourceIdentifier FileCreated -ErrorAction SilentlyContinue
$folder = 'X:\PicsSource\' #Folder to monitor
$destination = 'X:\PicsDest\' #Folder to copy files too
$filter = '*.jpg' #Set this for filtering of file types
$fsw = New-Object IO.FileSystemWatcher $folder, $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
$newname = get-item $path | Select @{Name="CreationTime";Expression= {"{0:yyyy}-{0:MM}-{0:dd}@{0:HH}{0:mm}_{0:ss}" -f ([DateTime]$_.CreationTime)}}
#Write-host $newname
$newname = $newname -replace "@{CreationTime="
$newname = $newname -replace "}"
#Write-Host $newname
#Write-Host $destination
#Write-Host $path
$finaldest = $destination + $newname
$finaldest = $finaldest + ".JPG" | Out-String
$finaldest = $finaldest.Trim()
write-host $finaldest
Copy-Item -path $path -Destination $finaldest -Force -Verbose
}
The creationtime string is a bit messy, but its functional I believe (happy to see improvements though!)
Thanks in advance, I'm keen to get this resolved :)
Cheers, Matt
Upvotes: 3
Views: 1169
Reputation: 1
My guess is that your script block to handle the created event is throwing an exception and the FileSystemWatcher is never calling it again. Any unhandled exception coming from that event will cause the FileSystemWatcher to never call your event again.
My next guess is that there is a timing issue with the event being called. The Created event fires when a file is created in a directory, so the event may get fired before the data is written and the file is closed. Maybe folder 2 has some really big files that get written slowly?
Anyway, one of the common solutions is to simply retry the file copy if there is some error writing the file. See if the following event code works for you:
$onCreated = Register-ObjectEvent $fsw Created -SourceIdentifier FileCreated -Action {
$path = $Event.SourceEventArgs.FullPath
$newname = (Get-Item $path).CreationTime.ToString("yyyy-MM-dd@HHmm_ss_fff")
$finaldest = $destination + $newname + ".JPG"
$copied = $false
$retriesLeft = 10
while(-not $copied -and ($retriesLeft -gt 0)) {
try {
Copy-Item -path $path -Destination $finaldest -Force -Verbose
$copied = $true
} catch {
Start-Sleep -Milliseconds 500
--$retriesLeft
}
}
if (-not $copied) {
# log an error with $path and $finaldest
}
}
Basically what this does is tries to copy and if that fails it will sleep for 500 milliseconds and then try again. It has a maximum of 10 retries before it gives up for good. I also tacked on milliseconds to the resulting file name because in my tests files that came in fast were being overwritten when the granularity was at seconds.
Upvotes: 0