Google
 

Wednesday, May 13, 2009

More great things about Visual Web Developer 2008 Express

In case you have not installed it yet: In addition to the great features Visual Web Developer 2008 Express has. SP1 is even much better!!

It now has more project tamplates. The most important one is the web application template (I hate the Website template).
You can also add class library project to the same solution!!


Download and enjoy!!

Saturday, May 2, 2009

Downloading files using Windows PowerShell with progress information

Downloading a file using PowerShell is very easy, you just need to call WebClient.DownloadFile method from the .net framework. But let's make things more interesting.

I need a script to download a list of files whose URLs are specified in a file to local folder. And the script should check if the file already exists. If it is, it should skip to the next file. To give a better user experience, a (progress bar) will be displayed to notify the user about the progress.
Error handling and reporting is important. So we'll take care of it inside the script.

Let's start analyzing how to accomplish this.

The code:
The first line of code defines the script parameters which are inputFile: the path of the file that contains the list of URLs and folder where we'll download files to.

param ([string] $inputFile,[string]$folder)

We make some basic input validation to check if parameters are set:

trap {Write-Host "Error: $_" -Foregroundcolor Red -BackGroundColor Black;exit}

if([string]::IsNullOrEmpty($folder))
{
throw "folder parameter not set";
}

if([string]::IsNullOrEmpty($inputFile))
{
throw "inputFile parameter not set";
}


The above code starts with defining an error handler that will run when a stopping error occurs in the script (in the current scope). It simply say: When an error occurs, write it to the host and exit the script.
Note that I pass Foregroundcolor and BackGroundColor parameter to the Write-Host Cmdlet so the user gets the same experience he gets with other PowerShell errors.
The next validation code simply check if the parameters are passed. If not, an error is thrown.

Next, we read the contents of the input file:

$files = Get-Content $inputFile -ErrorAction Stop

I use the Get-Content Cmdlet specifying the ErrorAction parameter = Stop. ErrorAction is a common parameter for PowerShell Cmdlets. The Stop value asks PowerShell to consider any errors fatal errors that will stop the script. But as I defined the trap handler as shown above, the same error handling will apply. The error message will be displayed and the script will exit.

The next line creates a WebClient object to be used in the download porcess:

$web = New-Object System.Net.WebClient

Next, I initialize a counter to be used in progress display based on the number of files downloaded so far. The a for loop is used to iterate on files.
Note that I define another trap handler that will act whenever an error is thrown within the for loop scope. It calls a specific function that handles download errors then continues the loop:

foreach($file in $files)
{
trap {ErrorHandler($_);continue}
.
.
.
}


Inside the loop, I create the download file path and display the progress using Write-Progress Cmdlet.

$path = [IO.Path]::Combine($folder,$file.SubString($file.LastIndexOf("/")+1));

Write-Progress -Activity "downloading" -Status $path -PercentComplete (($i / $files.Length)*100)


And if the file does not exit, DownloadFile is called.

if([System.IO.File]::Exists($path) -eq $False )
{
$web.DownloadFile($file,$path)
}


I used the script to download presentations from the MIX 2009 conference. The attached ZIP file includes both the PowerShell script and the file that contains the URLs)




Complete code listing:

param ([string] $inputFile,[string]$folder)

function ErrorHandler($error)
{
Write-Host "Error while downloading file:$file" -Foregroundcolor Red -BackGroundColor Black
Write-Host $error -Foregroundcolor Red -BackGroundColor Black
Write-Host ""
}

trap {Write-Host "Error: $_" -Foregroundcolor Red -BackGroundColor Black;exit}

if([string]::IsNullOrEmpty($folder))
{
throw "folder parameter not set";
}

if([string]::IsNullOrEmpty($inputFile))
{
throw "inputFile parameter not set";
}


$files = Get-Content $inputFile -ErrorAction Stop

$web = New-Object System.Net.WebClient
$i = 0
foreach($file in $files)
{
trap {ErrorHandler($_);continue}

$path = [IO.Path]::Combine($folder,$file.SubString($file.LastIndexOf("/")+1));

Write-Progress -Activity "downloading" -Status $path -PercentComplete (($i / $files.Length)*100)



if([System.IO.File]::Exists($path) -eq $False )
{
$web.DownloadFile($file,$path)
}

$i = $i+1
}