First to clarify this question is aimed to HTTP(s) download .For FTP may be I'll ask (and answer) another question.
Here are some similar questions - but I want to be more precise .
Besides excluding external tools I want the solution(s) to be applicable for the widest possible types of windows machines (including XP,Win2003,Vista which still have big enough share).
Also as WSH
is one of the possible options I prefer no using of temp files and everything to be packed in a single .bat
file (which is possible with both jscript and vbscript).
What are possible approaches.
- "Pure" batch solution with BITSADMIN - a command line utility
available on every windows machine .It's not pretty convenient but is an/the only option where
no other scripting language should be used.
- Using WSH - Three approaches are possible - WinHTTP , MSXML2.XMLHTTP
,InternetExlorer.Application - all of them are accessible ActiveX
objects in order of how I prefer them.WinHTTP and MSXML2.XMLHTTP
are pretty similar in their capabilities but WinHTTP has a
reputation of more stable.InternetExlorer.Application is in fact
just the Internet explorer accessible through ActiveX object and
some UI elements are unavoidable (are they?) so I'll skip this one.
- Using .NET - It's possible to create a hybrid batch file with all
the three default .NET compilers (Jscript.net , VB.Net , C#) with
Jscript.net there is no redundant error messages so I'll prefer
it.If we ignore the fact that there's a compiled .exe all the code
is in one file ,so according to me this fits in the requirements :-)
.With .NET we can use System.Net.WebClient or
System.Net.HttpWebRequest (the WebClient relies on it) or
System.Web.HttpRequest , but for now I'll post only
System.Net.WebClient solution.And even more same ActiveX objects
accessible with WSH are available here too.So there are really many
ways to dowanload a file with .Net.May be in the future I'll update
my answer.Anyway only the Webclient is especially designed for download.
- Using powershell - has same possibilities as .NET but with less
chances to be installed on all machines you can meet.So I'll skip
this one too.
The answers.All scripts should be saved with .bat
/.cmd
extensions and can be used directly as batch scripts.
1) Certutuil:
certutil.exe -urlcache -split -f "https://download.sysinternals.com/files/PSTools.zip" pstools.zip
CertUtil command can be abused to download a file from internet.Available by default in windows since Vista.For WinXP Server 2003 Administration Tools are needed.
2) Bitsadmin :
simplest possible way to use it
bitsadmin /transfer myDownloadJob /download /priority normal http://downloadsrv/10mb.zip c:\10mb.zip
Or (you eventually will need this if you want to add credentials , proxy and etc.)
@echo off
setlocal
:: uses bitsadmin utility to download a file
:: bitsadmin is not available in winXP Home edition
:: the only way to download a file with 'pure' batch
:download
if "%2" equ "" (
call :help
exit /b 5
)
if "%1" equ "" (
call :help
exit /b 6
)
set url=%~1
set file=%~2
rem ----
if "%~3" NEQ "" (
set /A timeout=%~3
) else (
set timeout=5
)
bitsadmin /cancel download >nul
bitsadmin /create /download download >nul
call bitsadmin /addfile download "%url%" "%CD%\%file%" >nul
bitsadmin /resume download >nul
bitsadmin /setproxysettings download AUTODETECT >nul
set /a attempts=0
:repeat
set /a attempts +=1
if "%attempts%" EQU "10" (
echo TIMED OUT
endlocal
exit /b 1
)
bitsadmin /info download /verbose | find "STATE: ERROR" >nul 2>&1 && endlocal && bitsadmin /cancel download && echo SOME KIND OF ERROR && exit /b 2
bitsadmin /info download /verbose | find "STATE: SUSPENDED" >nul 2>&1 && endlocal && bitsadmin /cancel download &&echo FILE WAS NOT ADDED && exit /b 3
bitsadmin /info download /verbose | find "STATE: TRANSIENT_ERROR" >nul 2>&1 && endlocal && bitsadmin /cancel download &&echo TRANSIENT ERROR && exit /b 4
bitsadmin /info download /verbose | find "STATE: TRANSFERRED" >nul 2>&1 && goto :finishing
w32tm /stripchart /computer:localhost /period:1 /dataonly /samples:%timeout% >nul 2>&1
goto :repeat
:finishing
bitsadmin /complete download >nul
echo download finished
endlocal
goto :eof
:help
echo %~n0 url file [timeout]
echo.
echo url - the source for download
echo file - file name in local directory where the file will be stored
echo timeout - number in seconds between each check if download is complete (attempts are 10)
echo.
goto :eof
3) - WinHttp and WSH (SSL/certificate and Proxy options have been never tested ...). Here's a ready to use script that uses WinHttpRequest .It can perform whole range of http requests and can be used for downloading of files too (not too big files).If you need you can also add your own authentication headers.
call winhhtpjs.bat https://example.com/files/some.zip -saveTo c:\somezip.zip
4) MSXML2.XMLHTTP and WSH (better use WinHTTP)(SSL/certificate and Proxy options have been never tested ...)
@if (@X)==(@Y) @end /* JScript comment
@echo off
rem :: the first argument is the script name as it will be used for proper help message
cscript //E:JScript //nologo "%~f0" "%~nx0" %*
exit /b %errorlevel%
@if (@X)==(@Y) @end JScript comment */
// used resources
//http://www.codeproject.com/Tips/506439/Downloading-files-with-VBScript
//http://blogs.msdn.com/b/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx
//https://msdn.microsoft.com/en-us/library/ie/ms535874(v=vs.85).aspx
//https://msdn.microsoft.com/en-us/library/aa923283.aspx
//https://msdn.microsoft.com/en-us/library/ms759148(v=vs.85).aspx
//https://msdn.microsoft.com/en-us/library/ms759148(v=vs.85).aspx
//https://msdn.microsoft.com/en-us/library/ms760236(v=vs.85).aspx
//http://stackoverflow.com/questions/20712635/providing-authentication-info-via-msxml2-serverxmlhttp
//https://msdn.microsoft.com/en-us/library/ms763680(v=vs.85).aspx
//https://msdn.microsoft.com/en-us/library/ms757849(v=vs.85).aspx
//http://fm4dd.com/programming/shell/microsoft-vbs-http-download.htm
//http://stackoverflow.com/questions/11573022/vba-serverxmlhttp-https-request-with-self-signed-certificate
//http://www.qtcentre.org/threads/44629-Using-XMLHttpRequest-for-HTTPS-Post-to-server-with-SSL-certificate
// global variables and constants
var ARGS = WScript.Arguments;
var scriptName=ARGS.Item(0);
var url="";
var saveTo="";
var user=0;
var pass=0;
var proxy=0;
var bypass="";
var proxy_user=0;
var proxy_pass=0;
var certificate=0;
var force=true;
//ActiveX objects
//Use the right version of MSXML
/*var progIDs = [ 'Msxml2.DOMDocument.6.0', 'Msxml2.DOMDocument.5.0', 'Msxml2.DOMDocument.4.0', 'Msxml2.DOMDocument.3.0', 'Msxml2.DOMDocument' ]
for (var i = 0; i < progIDs.length; i++) {
try {
var XMLHTTPObj = new ActiveXObject(progIDs[i]);
}catch (ex) {
}
}
if typeof XMLHTTPObj === 'undefined'{
WScript.Echo ("You are using too ancient windows or you have no installed IE");
WScript.Quit(1);
}*/
var XMLHTTPObj = new ActiveXObject("MSXML2.XMLHTTP");
var FileSystemObj = new ActiveXObject("Scripting.FileSystemObject");
var AdoDBObj = new ActiveXObject("ADODB.Stream");
function printHelp(){
WScript.Echo(scriptName + " - downloads a file through HTTP");
WScript.Echo(scriptName + " url localfile [-force yse|no] [-user username -password password] [-proxy proxyserver:port -bypass bypass_list]");
WScript.Echo(" [-proxyuser proxy_username -proxypassword proxy_password] [-certificate certificateString]");
WScript.Echo("-force - decide to not or to overwrite if the local exists");
WScript.Echo("proxyserver:port - the proxy server");
WScript.Echo("bypass- bypass list can be \"\" if you don't need it");
WScript.Echo("proxy_user , proxy_password - credentials for proxy server");
WScript.Echo("user , password - credentials for the server");
WScript.Echo("certificate - location of SSL certificate");
WScript.Echo("Example:");
WScript.Echo(scriptName +" http://somelink.com/somefile.zip c:\\somefile.zip -certificate \"LOCAL_MACHINE\\Personal\\My Middle-Tier Certificate\"");
}
function parseArgs(){
//
if (ARGS.Length < 3) {
WScript.Echo("insufficient arguments");
printHelp();
WScript.Quit(43);
}
url=ARGS.Item(1);
saveTo=ARGS.Item(2);
if(ARGS.Length % 2 != 1) {
WScript.Echo("illegal arguments");
printHelp();
WScript.Quit(44);
}
for (var i=3;i<ARGS.Length-1;i=i+2){
if(ARGS.Item(i).toLowerCase=="-force" && ARGS.Item(i+1)=='no'){
force=false;
}
if(ARGS.Item(i).toLowerCase=="-user"){
user=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-password"){
pass=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-proxy"){
proxy=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-bypass"){
bypass=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-proxyuser"){
proxy_user=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-proxypassword"){
proxy_pass=ARGS.Item(i+1);
}
if(ARGS.Item(i).toLowerCase=="-certificate"){
certificate=ARGS.Item(i+1);
}
}
}
function existsItem(path){
return FileSystemObj.FolderExists(path)||FileSystemObj.FileExists(path);
}
stripTrailingSlash = function(path){
while (path.substr(path.length - 1,path.length) == '\\') {
path=path.substr(0, path.length - 1);
}
return path;
}
function deleteItem(path){
if (FileSystemObj.FileExists(path)){
FileSystemObj.DeleteFile(path);
return true;
} else if (FileSystemObj.FolderExists(path) ) {
FileSystemObj.DeleteFolder(stripTrailingSlash(path));
return true;
} else {
return false;
}
}
function writeFile(fileName,data ){
AdoDBObj.Type = 1;
AdoDBObj.Open();
AdoDBObj.Position=0;
AdoDBObj.Write(data);
AdoDBObj.SaveToFile(fileName,2);
AdoDBObj.Close();
}
function download( url,file){
if (force && existsItem(file)){
if(!deleteItem(file)){
WScript.Echo("Unable to delete "+ file);
WScript.Quit(8);
}
}else if (existsItem(file)){
WScript.Echo("Item " + file + " already exist");
WScript.Quit(9);
}
if (proxy!=0 && bypass !="") {
//https://msdn.microsoft.com/en-us/library/ms760236(v=vs.85).aspx
XMLHTTPObj.setProxy(SXH_PROXY_SET_DIRECT,proxy,bypass);
} else if (proxy!=0) {
XMLHTTPObj.setProxy(SXH_PROXY_SET_DIRECT,proxy,"");
}
if (proxy_user!=0 && proxy_pass!=0 ) {
//https://msdn.microsoft.com/en-us/library/ms763680(v=vs.85).aspx
XMLHTTPObj.setProxyCredentials(proxy_user,proxy_pass);
}
if(certificate!=0) {
//https://msdn.microsoft.com/en-us/library/ms763811(v=vs.85).aspx
WinHTTPObj.setOption(3,certificate);
}
if (user!=0 && pass!=0){
//https://msdn.microsoft.com/en-us/library/ms757849(v=vs.85).aspx
XMLHTTPObj.Open('GET',url,false,user,pass);
} else {
XMLHTTPObj.Open('GET',url,false);
}
XMLHTTPObj.Send();
var status=XMLHTTPObj.Status
switch(status){
case 200:
WScript.Echo("Status: 200 OK");
break;
case 401:
WScript.Echo("Status: 401 Unauthorized");
WScript.Echo("Check if correct user and password were provided");
WScript.Quit(401);
break;
case 407:
Wscript.Echo("Status:407 Proxy Authentication Required");
Wscript.Echo("Check if correct proxy user and password were provided");
WScript.Quit(407);
break;
default:
Wscript.Echo("Status: "+status);
WScript.Echo("Try to help yourself -> https://en.wikipedia.org/wiki/List_of_HTTP_status_codes");
WScript.Quit(status);
}
writeFile(file,XMLHTTPObj.ResponseBody);
}
function main(){
parseArgs();
download(url,saveTo);
}
main();
5) .NET and webclient (Here's no SSL option.Will try to add it. Poxy options have never been tested)
@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
set "jsc=%%v"
)
::if not exist "%~n0.exe" (
"%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
::)
%~n0.exe %*
endlocal & exit /b %errorlevel%
*/
//todo SSL Support
//todo Better help message
//todo check if local file exists
import System;
import System.Net.WebClient;
import System.Net.NetworkCredential;
import System.Net.WebProxy;
import System.Uri;
import System.Security.Cryptography.X509Certificates;
var arguments:String[] = Environment.GetCommandLineArgs();
var url=0;
var toFile=0;
var force=true;
var user=0;
var password=0;
var proxy=0;
var bypass=0;
var proxy_user=0;
var proxy_pass=0;
var certificate=0;
function printHelp(){
Console.WriteLine(arguments[0] + "download from url to a file");
Console.WriteLine(arguments[0] + "<url> <file> [-user user -password password] [-proxy proxy] [-proxy_user proxy.user -proxy_pass proxy.pass]");
}
function parseArgs(){
if (arguments.length < 3) {
Console.WriteLine("Wrong arguments");
printHelp();
Environment.Exit(1);
}
if (arguments.length %2 != 1) {
Console.WriteLine("Wrong number arguments");
printHelp();
Environment.Exit(2);
}
url=arguments[1];
toFile=arguments[2];
for (var i=3;i<arguments.length-1;i=i+2){
var arg=arguments[i].ToLower();
switch (arg){
case "-user" :
user=arguments[i+1];
break;
case "-password" :
password=arguments[i+1];
break;
case "-proxy" :
proxy=arguments[i+1];
break;
case "-proxy_user" :
proxy_user=arguments[i+1];
break;
case "-proxy_pass" :
proxy_pass=arguments[i+1];
break;
case "-bypass" :
bypass=[arguments[i+1]];
break;
/*case "-certificate" :
certificate=arguments[i+1];
break;*/
default:
Console.WriteLine("Invalid argument "+ arguments[i]);
printHelp();
Environment.Exit(3);
}
}
}
function download(){
var client:System.Net.WebClient = new System.Net.WebClient();
if (user!=0 && password!=0){
client.Credentials=new System.Net.NetworkCredential(user, password);
}
if (proxy!=0){
var webproxy =new System.Net.WebProxy();
webproxy.Address=new Uri(proxy);
if (proxy_user!=0 && proxy_pass!=0){
webproxy.Credentials=new System.Net.NetworkCredential(proxy_user,proxy_pass);
}
webproxy.UseDefaultCredentials =false;
if (bypass!=0){
webproxy.BypassList=bypass;
webproxy.BypassProxyOnLocal = false;
}
client.Proxy=webproxy;
}
try {
client.DownloadFile(arguments[1], arguments[2]);
} catch (e) {
Console.BackgroundColor = ConsoleColor.Green;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\n\nProblem with downloading " + arguments[1] + " to " + arguments[2] + "Check if the internet address is valid");
Console.ResetColor();
Environment.Exit(5);
}
}
parseArgs();
download();