My First Powershell SnapIn – Get-Url (wget)

There are already a lot of people who have solved this problem, but I wanted to solve it too. Powershell doesn’t ship with a wget‘ish cmdlet, at least that I could easily find. So I decided to kill two birds with one stone, create my first SnapIn and create a Get-Url cmdlet. It’s much easier than I thought.

Getting started

Start by creating a new class library project. There are two references you are going to need, System.Configuration.Install and System.Management.Automation. The former ships with .NET. The latter ships with the Windows SDK.

snapinreferences

Create the snap-in class

Add a class for the snap in. This class is fairly worthless, but nevertheless it is required. It helps the installer know what needs to be installed, and what the SnapIn will be called.

[RunInstaller(true)]
public class WebSnapIn : PSSnapIn
{
    public override string Description
    {
        get { return "Snap for web related activities"; }
    }
 
    public override string Name
    {
        get { return "WebSnapIn"; }
    }
 
    public override string Vendor
    {
        get { return "Brian Hartsock"; }
    }
}

Create the cmdlet

Now to the meat and potatoes, create the cmdlet. I don’t want to get into the details of everything that is necasary, just pay attention to the attributes and the ProcessRecord method. There are much better teachers at the intricacies of SnapIn creation than I.

[Cmdlet(VerbsCommon.Get, "url")]
public class GetUrl : Cmdlet
{
    public GetUrl()
    {
        Progress = new ProgressRecord(1, "Downloading...", "Starting...");
    }
 
    [Parameter(Mandatory=true, Position=0)]
    public Uri Url { get; set; }
 
    private ProgressRecord Progress { get; set; }
 
    protected override void ProcessRecord()
    {
        UpdateProgress("Connecting...");
 
        var request = WebRequest.Create(Url);
        var response = request.GetResponse();           
 
        UpdateProgress("Connected");
 
        using (var responseStream = response.GetResponseStream())
        using (var fileStream = new FileStream(GetFilePath(), FileMode.OpenOrCreate))
        {
            CopyStream(
                responseStream, 
                fileStream, 
                1024,
                response.ContentLength,
                (current, total) => UpdateDownloadProgress(current, total));
        }
    }
 
    private string GetFilePath()
    {
        var fileName = Url.Segments.Last();
        var fullPath = Path.Combine(Directory.GetCurrentDirectory(), fileName);
 
        return fullPath;
    }
 
    private void CopyStream(Stream source, Stream sink, int bufferSize, long sourceLength, Action<long, long> notification)
    {
        byte[] buffer = new byte[bufferSize];
 
        long numberOfBytesCopied = 0;
 
        while (true)
        {
            var actual = source.Read(buffer, 0, buffer.Length);
            if (actual == 0)
            {
                break;
            }
            sink.Write(buffer, 0, actual);
            numberOfBytesCopied += actual;
            notification(numberOfBytesCopied, sourceLength);
        }
    }
 
    private void UpdateProgress(string description)
    {
        Progress.StatusDescription = description;
 
        WriteProgress(Progress);
    }
 
    private void UpdateDownloadProgress(long current, long total)
    {
        int percent = (int)((100 * current) / total);
 
        Progress.StatusDescription = string.Format("{0} bytes of {1} ({2}%)", current, total, percent);
        Progress.PercentComplete = percent;
 
        WriteProgress(Progress);
    }
}

Build, and your almost ready to start using it.

Install and run

InstallUtil must be used to register the DLL, then you have to add the snap-in to the current session.

c:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe WebSnapIn.dll
 
add-snapin WebSnapIn
get-url http://prdownloads.sourceforge.net/nunit/NUnit-2.4.8-net-2.0.zip?download

Here it is in action, although the video is pretty terrible. Imagine the text is clear and you can see what is going on. (I am a video noob)


Get-Url in action from Brian Hartsock on Vimeo.

Lastly, my appologies to NUnit/SourceForge. I probably downloaded NUnit 30 times testing this script out.

Post to Twitter Post to Delicious Post to Digg Post to Facebook Post to Reddit

3 Comments to “My First Powershell SnapIn – Get-Url (wget)”

  1. Learning how to build snapins is great. Two things.
    Curious, you could download a file like below. You lose the progress bar though.

    Check out advanced functions http://tinyurl.com/92pzrf in CTP3. Has lots of the snapin capability but all in PowerShell script.

    $wc = New-Object net.webclient

    $wc.DownloadFile(“http://prdownloads.sourceforge.net/nunit/NUnit-2.4.8-net-2.0.zip?download”, “C:\NUnit-2.4.8-net-2.0.zip”)

  2. Ya. I actually used webclient before I started on this cmdlet. One thing I started noticing is large files would take an unknown amount of time. This was super annoying, since I didn’t know if it was going fast or slow.

    So a side goal was to get the progress bar.

    Even if you don’t want a cmdlet though, you can create the same logic in a simple function. Nothing in the cmdlet can’t be done directly in Powershell.

    I haven’t had a chance to check out CTP3, I need to put it on my list. Looks awesome.

  3. Looks nice Brian. Actually I wasn’t looking for this but this could be helpful for my new project about PHP on Windows. I want to use wget to download php from command line. But with get-url, I can do same thing on Powershell, I can use this :)

Leave a Reply