PowerShell is a great scripting language to write all kinds of scripts. But did you know that you can also download a file with PowerShell? You can use PowerShell to download single or multiple files from the internet.
There are a couple of methods when it comes to downloading files with PowerShell. We can download files from any URL with PowerShell, local network shares, and from behind credential protected websites.
In this article, we are going to start with the most straightforward method to download a single file and we are also going to take a look at other (faster) methods to download a file with PowerShell.
Powershell Download File from URL
We are going to start with the most common way to download a file from an URL with PowerShell. For this, we will be using the Invoke-WebRequest
cmdlet. To download a file we need to know the source URL and give up a destination for the file that we want to download.
The parameter -OutFile
is required. You don’t need to enter the full path, but a file name is required.
# Source URL $url = "http://speed.transip.nl/10mb.bin" # Destation file $dest = "c:\temp\testfiles.bin" # Download the file Invoke-WebRequest -Uri $url -OutFile $dest
Invoke-WebRequest will overwrite the local file if it already exists without any warning
Authentication with Invoke-WebRequest
Some online resources require you to log in before you can access/download the files. With the Invoke-WebRequest cmdlet, we can provide the credentials that are needed for downloading the files.
If you are creating a script that will need to run automatically, then you will need to store the credentials in the script itself. I recommend creating a secure string password and store it in a text file on the computer that is running the script. It still won’t be super secure, but it’s a little bit better than using a plaintext password in your script.
# URL and Destination $url = "http://speed.transip.nl/10mb.bin" $dest = "c:\temp\testfiles" # Define username and password $username = 'LazyUsrName' $password = 'StrongPlainTextPasswd' # Convert to SecureString $secPassword = ConvertTo-SecureString $password -AsPlainText -Force # Create Credential Object $credObject = New-Object System.Management.Automation.PSCredential ($username, $secPassword) # Download file Invoke-WebRequest -Uri $url -OutFile $dest -Credential $credObject
Download files faster with Start-BitsTransfer in PowerShell
The Invoke-WebRequest method is available in all PowerShell versions and can also be used on Linux machines. But the downside is that it’s a bit slow. With Invoke-WebRequest, the file is buffered in the memory first, before it’s written to the disk.
A faster and better way is to use the Start-BitsTransfer cmdlet in PowerShell. This cmdlet allows you to queue files, set priority (useful for bandwidth limitation), can run in the background and download multiple files asynchronous.
This is the most basic method of downloading a file with BitsTransfer, you only need a source and destination.
# URL and Destination $url = "http://speed.transip.nl/10mb.bin" $dest = "c:\temp\testfiles" # Download file Start-BitsTransfer -Source $url -Destination $dest
By default, the download jobs run in the foreground consuming the maximum bandwidth available. You can change this by setting the priority of the job:
- Foreground – Default
- High
- Normal
- Low
Only the idle network bandwidth is used when you set the priority to high, normal, or low.
Another option is to run the download job asynchronous, allowing you to start multiple download jobs at the same time. When you use this method, make sure that you complete the download job when it’s finish.
Start-BitsTransfer -Source $url -Destination $dest -Asynchronous -Priority normal
As you can see I have downloaded the same bin file as before. But if we look in the destination folder we only see a .tmp file.
You will need to run Complete-BitsTransfer to finish the download job.
Get-BitsTransfer | Complete-BitsTransfer
Downloading Multiple Files with PowerShell
To download multiple files with PowerShell we first need to know which files are available. We can use the Invoke-WebRequest cmdlet first to get the content from the webpage.
If you take a look at the content of http://speed.transip.nl then you will see a list of binary files that we can download.
First, we are going to scrape the website
$content = Invoke-WebRequest -URI "http://speed.transip.nl"
This will return not only the content of the webpage but also other properties, like Links and InputFields.
This is a pretty simple webpage, but let’s say we only want the files that start with the name “random”. We can filter the links with a simple like query and select only the href property from each link.
$randomBinFiles = $content.links | where {$_.innerHTML -like 'random*'} | select href
So we now have the links for all random binary files. All we need to do is download each one of them.
When you need to download multiple files it’s better to use the Start-BitsTransfer cmdlet. It allows you to download multiple files simultaneously in the background with the parameter -Asynchronous
Other advantages of the BitsTransfer cmdlet is it can handle connection interruptions and is aware of your network bandwidth usage.
$url = "http://speed.transip.nl" # Create full links for each entry $randomBinFiles.foreach( { $_.href = $url + "/" + $_.href }) # Download each file in the background $randomBinFiles.foreach({ Start-BitsTransfer ($url + "/" + $_.href) -Asynchronous }) # Complete the BitsTransfer Get-BitsTransfer | Complete-BitsTransfer
We can start all the download jobs by using the parameter –Asynchronous
. Without it, the BitsTransfer cmdlet downloads the first file completely before starting the next download while putting your script on hold in the meantime.
You can use the Get-BitTransfer cmdlet to show the progress of the download. If you want to stop the download job then use the Remove-BitTransfer cmdlet. You can stop a single job based on its JobId or all jobs with:
Get-BitsTransfer | % {Remove-BitsTransfer $_.JobId}
Complete the BitsTransfer download
It’s important to complete the transferred file when using BitsTransfer in combination with the Asynchronous
parameter. When using Asynchronous it creates a temp file during the download process. But to actually use the file you will need to run the following cmdlet:
Get-BitsTransfer | Complete-BitsTransfer
PowerShell Download file from Server
We won’t be using the Invoke-WebRequest to download files from a local network source, like a server or NAS, with PowerShell. Instead, we can simply use the Copy-Item cmd to download a file from a server.
The Copy-Items cmdlet takes a source and destination, just like the Invoke-WebRequest cmdlet.
# Set Source and destination $source = "\\LA-WIN10-LAB01\speedtest\" $destination = "c:\temp\testfiles" # Copy all items in the source folder Copy-Item -path $source -Destination $destination -Recurse
If you want to know more about the Copy-Item cmdlet, then you should read this article where I explain more about the cmdlet and alternatives.
Powershell Download Zip File
The method to download zip files is pretty much the same as a normal file. But I wanted to show you how that downloads and extracts the zip file. This way you can immediately process the files inside the zip file without manual interaction.
I am going to use this sample csv on GitHub which we can download in a zip file. We have to set a destination for the zip file itself and a path where we want to extract the files to.
The Invoke-WebRequest downloads the zip file just like any other file.
# URL and Destination $url = "https://github.com/datapackage-examples/sample-csv/archive/refs/heads/master.zip" # Create temp destination for the zip and get zipfile name from source URL $zipFile = "c:\temp\zipfiles" + $(Split-Path -Path $Url -Leaf) # Extract path $extractPath = "c:\temp\zipfiles" # Download file Invoke-WebRequest -Uri $url -OutFile $zipFile
The next step is to extract the zip file automatically in the desired location. For this we are going to use a COM object. With the COM object we can extract the zip file and copy the content to the desired location.
# Create instance of COM Object $objShell = New-Object -ComObject Shell.Application # Extract the Files $extractedFiles = $ObjShell.NameSpace($zipFile).Items() # Copy the extracted files to the destination folder $ObjShell.NameSpace($extractPath).CopyHere($extractedFiles)
Wrapping Up
Downloading files with PowerShell is pretty easy when you have the exact URL of the source file. When you need to scrape a website first then it can be a little bit more work to set up properly.
Try to use the Start-BitsTransfer cmdlet for downloading files and set the priority to normal when using it in an autonouse script. BitsTransfer has more option when it comes to retries, resuming and bandwidth control then Invoke-WebRequest.
If you have any questions about how you can download a file with PowerShell, then drop a comment below.
How about showing us how to do this using a Client Authentication Certificate?
Rudy,
I was using Start-BitsTransfer to download for months now and a few days back (possibly after a Windows update) the downloads are blocked and errors out (not sure what changed). Setting the ExecutionPolicy on PowerShell will fix the problem (or running PowerShell as Admin) which I’d really rather not do. Invoke-Webrequest does work without altering the security settings from Windows/PowerShell default “RemoteSigned” for “CurrentUser” but I liked the fact that Start-BitsTransfer transferred the file without altering the modified date. Is there a way to authorize the script using Start-BitsTransfer to allow downloads? Does the Invoke-Webrequest allow virus scanners to function properly and that’s why it still functions with my current ExecutionPolicy settings?
Hi,
Is there any way to set an item property for the files downloaded? I have a scenario where i need to update the file metadata such as date or ID(reading from a txt file) to the downloaded files in a folder.
Any help would be appreciated
Thanks
Hi,
Thanks for the clear and concise article!
Do you know if there is a way to download a file that requires accepting terms and conditions? For example, the link to a zip file I’m trying to download takes me to a webpage where I have to click the “Accept” button for the download to begin. Can powershell access the url, click the “Accept” button, then output the file?
Yes that should be possible: https://docs.microsoft.com/en-us/archive/msdn-magazine/2008/march/test-run-web-ui-automation-with-windows-powershell
Hello, I want to use the Authentication with Invoke-WebRequest you mentioned in the webpage. I want to use a file that needs to be logged in to a website, and then the file can be downloaded after logging in to the website, because the code you tested is used here. is not executable
What do you mean with “the code you tested is used here. is not executable”
thank you for all the options
Invoke-WebRequest isn’t in all powershell versions. Specifically, anything before 3.0 so by default Windows 7 doesn’t have access to it.
Hi,
Thanks for this. I plan to use this in conjunction with Windows task scheduler to download a fresh file every week. I do not wish to overwrite the previous files. How do I modify the Invoke-Webrequest script to do this?
Thanks.
I would add the date to the destination file :
$dest = "c:\temp\testfiles-$(get-date -f yyyy-MM-dd).bin"
Please remove the left side social media sharing button. It is hindering the blog content and making it pretty irritating. By the way, nice content.
Thanks for letting me know. Issue is now fixed on smaller screensizes.