Downloading files with VBScript





4.00/5 (1 vote)
How to download files with VBScript using COM (WinHTTP and MSXML) or WGET
Introduction
With this article, we pretend to show you how to download files in VBScript with COM access (WinHTTP, MSXML.XMLHTTP) and with command line (WGET). You can choose what option will be more useful to you, and this post is here to clarify it.
Background
Downloading files is something really basic today, because it is really hard to find someone that does not have internet access. You may want to do an automatic update for your program, or to make download of a file only if a user need a specific function from your code, decreasing download of you script.
Options available
The COM technology allows another program access resources from another program. It is a Microsoft Technology and a really useful one. We can access Active Directory with it and add users and groups without opening the Management Console. We can download and install updates automatically with Windows Update API without accessing Microsoft Update site. We can write and save a document in Microsoft Word in command line.
And the better of all: It does not matter the programming language that i am using. I can do that with PHP, VBScript, Javascript, C#... The programming language only needs to support COM access and running in a Windows environment.
Some COM functions are bult-in with Microsoft Windows, another you need to download and install it. COM functions is better because, in general, we do not need to redistribute any 3rd party code because Windows already have it.
WinHTTP | MSXML.XMLHTTP | WGET | |
---|---|---|---|
COM access | Yes | Yes | No |
Minimum Requirements | Windows 2000 (with SP3) | Windows 95 and Internet Explorer 4.0 | Windows 95 and Internet Explorer 4.0 |
Get header without download | No | No* | Yes |
Can show download progress in real-time | No* | No* | Yes** |
Check local file version and download new file only if necessary. | No | No | Yes |
HTTPS support. | Yes | No | Yes |
FTP support. | No | No | Yes |
Support authentication (User/Password). | Yes | Yes | Yes |
* It is available, but we can't access it from VBScript (We need to use .NET instead).
** VBScript stops if we try assync command-line read with WGET. We need to show the command line prompt to show progress (It can't be viewed on the same window from script).
I hope the table below helps you choose the method you will use to download files.
WinHTTP and MSXML does not need you to redistribute anything with your code, but you lost some interesting functions from WGET for Win32 (it have much more functions than it! Read WGET documentation!). If you need to read the header from page or "Check local file version [...]" may be useful for you, choose it. WGET support this function natively, but we can implement it in .NET reading header and getting Content-Length and comparing it with Local file.
If WGET is discarded, you can choose WinHTTP or MSXML.
If you do not need to support Windows 9x, choose the 1st one. It is because MSXML, to download internet files in Windows Server versions of Windows, need manual change from Internet Explorer Security Zone, allowing local programs to access external resources (Security > Trusted Sites > Access data sources across domains). If we do not do that, our script will return "Access is denied." and will be closed.
I really recommends WinHTTP, only if you do not care that your script will not run in Windows Server versions without you ask for user to change Security Settings, making the server less secure.
Using WGET
WGET does not have COM access, but we can call command-line from our script and use it.
strScriptFile = Wscript.ScriptFullName ' C:\download.vbs
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strScriptFile)
strFolder = objFSO.GetParentFolderName(objFile) ' C:
Set objShell = CreateObject("WScript.Shell")
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
' Use strFolder to save on the same location of this script.
strSaveTo = "C:\"
' WGet saves file always on the actual folder. So, change the actual folder for C:\, where we want to save file
objShell.CurrentDirectory = strSaveTo
' "C:\wget.exe" "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab" -N
objShell.Run Quotes(strFolder & "\wget.exe") & " " & Quotes(strLink) & " -N",1,True
' -N: Continue download only if the local version is outdated.
objShell.CurrentDirectory = strFolder
' Add Quotes to string
' http://stackoverflow.com/questions/2942554/vbscript-adding-quotes-to-a-string
Function Quotes(strQuotes)
Quotes = chr(34) & strQuotes & chr(34)
End Function
In the first 4 lines, we get the folder that our script is saved (and wget.exe is located).
Then, we define two strings: strLink
, that is the link that we will download, and strSaveTo
, where file will be saved.
After that, we change the actual folder to the folder where we want to save the file. WGet saves the file on the actual folder that we are using, than it will save the file there.
We called WGet with objShell.Run
. It have 3 args:
1. Command to Execute.
2. Window mode. Use 0
to hide, 1
to show (in another Window). More options: http://ss64.com/vb/run.html
3. Wait for Finish. Use True
to wait and only continue processing our script after finish download, False
to continue processing script while download is made.
We have sent args for WGet:
-N
Download file only if local version is outdated.
With our code, it will open a 2nd window, showing download progress.
But there is some cases that we can not open a second prompt. (Silent script). We have an alternative: Hide prompt and show progress on the same Window. There is a problem: We lose the real-time view of our download. We have to wait download finish to show output.
We need to use the code below for this case.
strScriptFile = Wscript.ScriptFullName ' C:\download.vbs
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.GetFile(strScriptFile) ' Get file information from our Script.
strFolder = objFSO.GetParentFolderName(objFile) ' Get only folder from our script (C:)
Set objShell = CreateObject("WScript.Shell") ' Expand variables from command prompt
systemroot = objShell.ExpandEnvironmentStrings("%systemroot%")
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
strSaveTo = "C:\"
' Use strFolder to save on the same location of this script
' WGet saves file always on the actual folder. So, change the actual folder for C:\, where we want to save file
objShell.CurrentDirectory = strSaveTo
' "C:\wget.exe" "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab" -N -o C:\Windows\Temp\output.txt
objShell.Run Quotes(strFolder & "\wget.exe") & " " & Quotes(strLink) & " -N -o " & systemroot & "\temp\output.txt",0,True
' -N: Continue download only if the local version is outdated.
objShell.CurrentDirectory = strFolder
Set objFile = objFSO.OpenTextFile(systemroot & "\temp\output.txt")
Do
line = objFile.ReadLine
WScript.Echo line
Loop While Not objFile.AtEndOfStream
objFile.Close
' http://stackoverflow.com/questions/2942554/vbscript-adding-quotes-to-a-string
Function Quotes(strQuotes)
Quotes = chr(34) & strQuotes & chr(34)
End Function
We will only comment what is new on this code.
We called objShell
to expand %systemroot%
variable from prompt. We will use it to call Command Line in the next lines.
We called WGet with objShell.Run
with 2 args:
-N
Download file only if local version is outdated.
-o
Send all messages from WGet to %systemroot%\temp\output.txt
We back to strFolder
location, and used objFSO
to read the file %systemroot%\temp\output.txt
. It will read all lines of file until the end of file, and then exit from file.
If you want to read Header, you need to replace objShell.Run with:
objShell.Run Quotes(strFolder & "\wget.exe") & " --server-response --spider " & Quotes(strLink) & " -o " & systemroot & "\temp\output.txt",0,True
You need to replace the content inside Do Loop with:
Do
line = objFile.ReadLine
WScript.Echo line
If InStr(line,"Content-Length") Then
strContentLength = Mid(line,19,Len(line))
' WScript.Echo strContentLength
End If
Loop While Not objFile.AtEndOfStream
Explaining: We use InStr
function to check if a string have some text. If the actual line have "Content-Length", it will get the value.
We use Mid
to read the string from the character 19 to the final of the string, removing "Content-Length" and maintaining only the size. We can use it to compare the size of the actual file with the server, for example. (Use -N
to let WGet make this automatically). You can read another information from header data using this method, just use the right values on Mid.
If you need to use Authentication, add --user=username --password='password'
to WGet call on objShell.Run
Using WinHTTP
WinHTTP have COM access and is a native component from Windows 2000 with SP3 and above. It means that you do not need to redistribute anything with your code if you just want to download a small file.
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
' Get file name from URL.
' http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab -> wsusscn2.cab
strSaveName = Mid(strLink, InStrRev(strLink,"/") + 1, Len(strLink))
strSaveTo = "C:\" & strSaveName
WScript.Echo "HTTPDownload"
WScript.Echo "-------------"
WScript.Echo "Download: " & strLink
WScript.Echo "Save to: " & strSaveTo
' Create an HTTP object
Set objHTTP = CreateObject( "WinHttp.WinHttpRequest.5.1" )
' Download the specified URL
objHTTP.Open "GET", strLink, False
' Use HTTPREQUEST_SETCREDENTIALS_FOR_PROXY if user and password is for proxy, not for download the file.
' objHTTP.SetCredentials "User", "Password", HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
objHTTP.Send
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(strSaveTo) Then
objFSO.DeleteFile(strSaveTo)
End If
If objHTTP.Status = 200 Then
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
With objStream
.Type = 1 'adTypeBinary
.Open
.Write objHTTP.ResponseBody
.SaveToFile strSaveTo
.Close
End With
set objStream = Nothing
End If
If objFSO.FileExists(strSaveTo) Then
WScript.Echo "Download `" & strSaveName & "` completed successfuly."
End If
I don't think that there is much things to explain here. You need to inform Download URL, then we get the file name from URL (with Mid, it is necessary for ADODB call (.SaveToFile
)), call WinHTTP, download file and save it with objStream.
Using MSXML.XMLHTTP
MSXML has two major versions: 3.0 and 6.0.
Version 6.0 is the most recent, and is built-in in Windows Vista+ and Server 2008+. It is compatible with Windows XP and Server 2003, but requires manual installation in this OS. You can redistribute the executable and install it (Recommended). If you do not do that, it will use Version 3.0, that is built-in in IE6.
Version 3.0 will be used for clients with Windows 2000 and 98 with IE6 installed.
Version 2.x will be used for clients with Windows 95 or 98 with IE lower than 6 installed.
Remember to warn Windows Server users that they need to change Internet Security Zone to allow scripts have internet access! (It decrease security if a malicious script be executed!)
strLink = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab"
' Get file name from URL.
' http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab -> wsusscn2.cab
strSaveName = Mid(strLink, InStrRev(strLink,"/") + 1, Len(strLink))
strSaveTo = "C:\" & strSaveName
WScript.Echo "HTTPDownload"
WScript.Echo "-------------"
WScript.Echo "Download: " & strLink
WScript.Echo "Save to: " & strSaveTo
' Create an HTTP object
Set objHTTP = CreateObject("MSXML2.XMLHTTP")
' Download the specified URL
'xmlhttp.Open "GET", strURL, false, "User", "Password"
objHTTP.open "GET", strLink, False
objHTTP.send
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(strSaveTo) Then
objFSO.DeleteFile(strSaveTo)
End If
If objHTTP.Status = 200 Then
Dim objStream
Set objStream = CreateObject("ADODB.Stream")
With objStream
.Type = 1 'adTypeBinary
.Open
.Write objHTTP.responseBody
.SaveToFile strSaveTo
.Close
End With
set objStream = Nothing
End If
If objFSO.FileExists(strSaveTo) Then
WScript.Echo "Download `" & strSaveName & "` completed successfuly."
End If
Points of Interest
With this alternatives, you can download a file using VBScript. I recommend you using WGet for large files, then the user won't think that the program stopped. If you can choose WinHTTP or MSXML, i recommend the first one, because it is more compatible (even without Windows 9x support, does not require attention in Windows Server edition).
You can use X-HTTP, curl or Internet Explorer API to download files, but i didn't tested then.
References
I do not own all knowledge of the world, and this article only was written because I want to help another people, like the people bellow does. I really recommend you read these references:
VBScript Scripting Techniques: Download Files by Rob van der Woude
HTTP-Download-vbs by Frank4DD
History
12 Dec 2012 Tip published.