0

I have a list of urls (urls.txt):

https://example.com/1.webp
https://example.org/bar2.webp
... 10k more

Files vary in size from 1kb to 100kb.

How can I download these files quickly on a Windows 10 machine without installing any third-party software?

I need it to be in a single file that user can double-click without installing any additional software.

It should work on any decently up-to-date Windows 10 PC. AFAIK it means the PowerShell version is 5.1.


Additional information.

I tried this:

powershell -Command "Invoke-WebRequest https://example.com/1.webp -OutFile 1.webp"

but it extremely slow due to sequential execution.

So far this works in PowerShell fast enough:

Get-Content .\urls.txt |ForEach-Object {
    $FileName = Split-Path -leaf $_
    Invoke-WebRequest $_ -OutFile $FileName
}

But I can't figure out how to invoke this script with a double-click on a file.

Invoking .ps1 file from a .bat file doesn't work. Error:

download.ps1 cannot be loaded because running scripts is disabled on this system.

Asking user to adjust permissions is not an option.

This works in a clickable .bat file:

powershell -command ^
Invoke-WebRequest https://example.com/1.webp -OutFile 1.webp;

But this script fails silently:

powershell -command ^
Get-Content .\urls.txt |ForEach-Object { ^
    $FileName = Split-Path -leaf $_ ^
    Invoke-WebRequest $_ -OutFile $FileName ^
} ^
stkvtflw
  • 12,092
  • 26
  • 78
  • 155

2 Answers2

1

You could try the foreach-object -parallel method for this case, i tried something simular once with multiple process starts for robocopy to get like 1000 small files (5-10kb) on another harddrive.

I will look up if i can find it again.

Edit 1: you can go over like this for example.

$allmylinks = import-csv -path "path to your csv"
foreach -parallel($link in $allmylinks){
    Invoke-WebRequest $link
}
Kevin
  • 193
  • 1
  • 4
  • 16
  • how do I iterate over a file lines with it? Sry, I never used Windows – stkvtflw Aug 12 '22 at 08:03
  • I'm getting this: "The '-parallel' parameter can be used only within a workflow." – stkvtflw Aug 12 '22 at 10:41
  • Update your PowerShell version. PowerShell version 5.1 and up includes the `-parallel` option for foreach. If you cannot do this, there's another way to do it but it requires much more code. – FoxDeploy Aug 12 '22 at 13:00
  • @FoxDeploy, `ForEach-Object -Parallel` requires PowerShell (Core) v7+; Windows PowerShell doesn't support it. – mklement0 Aug 12 '22 at 14:46
1

"...how do I iterate over a file lines with it? Sry, I never used Windows" (that must feel like me after a Linux machine).

Open a PowerShell prompt (Start → Run → PowerShell) or just type PowerShell.exe on the command prompt.

At the PowerShell prompt, to run the task in parallel using ForEach-Object -Parallel:

1..9 |ForEach-Object -Parallel { "Invoke-WebRequest https://example.com/$_.webp" -OutFile "$_.webp" }

Where "$_" is the current item (1to9`), you might also use a list here, like:

'One', 'Two', 'Three' |ForEach-Object -Parallel { ...

In case you "need to read it directly from the file", (presuming that you want use the name in the url as your filename) you might do something like this:

Get-Content .\urls.txt |ForEach-Object -Parallel {
    $FileName = Split-Path -leaf $_
    "Invoke-WebRequest $_ -OutFile $FileName
}

Update

(based on the additional information in your question and comments in this answer)

Final steps to making you command line easy to launch for novice user, taking in account that passing "complex" commands with special characters (as newlines, spaces and double quotes) from a batch file interpreter to PowerShell is quiet a hassle as there are a lot of exceptions on the exceptions. See: these stackoverflow questions

In your case it might be simply putting your commands in a single (quoted) command line and separate each syntax with a semi-colon (;):

powershell -command "Get-Content .\urls.txt |ForEach-Object { $FileName = Split-Path -leaf $_; Invoke-WebRequest $_ -OutFile $FileName }"

But to be on the safe side (in case e.g. a powershell command/parameter requires to be quoted by itself), I would rather supply robust solution which is encoding your command line to base64 and use the -EncodedCommand parameter. See also these answers:

Encoding

To encode your command line to base64:

$Command = {
    Get-Content .\urls.txt |ForEach-Object {
        $FileName = Split-Path -leaf $_
        Invoke-WebRequest $_ -OutFile $FileName
    }
}.ToString()
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Command)
[Convert]::ToBase64String($Bytes)

Download.bat

Including the encoded command line in a (single) batch file add the following command in you batch file where the base64 string is copied from the above ToBase64String conversion:

PowerShell -EncodedCommand CgAgACAAIAAgACAAIAAgACAARwBlAHQALQBDAG8AbgB0AGUAbgB0ACAALgBcAHUAcgBsAHMALgB0AHgAdAAgAHwARgBvAHIARQBhAGMAaAAtAE8AYgBqAGUAYwB0ACAAewAKACAAIAAgACAAIAAgACAAIAAgACAAIAAgACQARgBpAGwAZQBOAGEAbQBlACAAPQAgAFMAcABsAGkAdAAtAFAAYQB0AGgAIAAtAGwAZQBhAGYAIAAkAF8ACgAgACAAIAAgACAAIAAgACAAIAAgACAAIABJAG4AdgBvAGsAZQAtAFcAZQBiAFIAZQBxAHUAZQBzAHQAIAAkAF8AIAAtAE8AdQB0AEYAaQBsAGUAIAAkAEYAaQBsAGUATgBhAG0AZQAKACAAIAAgACAAIAAgACAAIAB9AAoAIAAgACAAIAA=
iRon
  • 20,463
  • 10
  • 53
  • 79
  • Sorry for misleading example, but the urls in the list are completely different, so I need to read it directly from the file. Can you adjust your answer pls? – stkvtflw Aug 12 '22 at 10:01
  • getting error "parameter set cannot be resolved using the specified named parameters" – stkvtflw Aug 12 '22 at 10:46
  • Sorry, the `-parallel` is only for [newer PowerShell versions](https://github.com/PowerShell/PowerShell). I would first try without as it migth already a lot faster then initiating this from a batch file and than possibly install/use the latest PowerShell version – iRon Aug 12 '22 at 10:58
  • thanks! one last question: how do I run this in a batch file? It works well without `-Parallel` in the power shell, but bat file does nothing. I've added the `powershell -command ^` statement and `^` at the end of each row in the PS command. – stkvtflw Aug 12 '22 at 11:37
  • You migth put it in a '.ps1' file and than right click on it, and select: ` Run with PowerShell ` – iRon Aug 12 '22 at 11:40
  • Btw, not sure if it is of any help, but the newer versions of PowerShell also run on other OSes (including macOS). – iRon Aug 12 '22 at 11:46
  • Can't use the "Run with PowerShell" option. Only a file that can be double clicked on any decently up-to-date windows PC – stkvtflw Aug 12 '22 at 11:56
  • You might create a `download.bat` file in the same directory and in there call your PowerShell script: `powershell.exe -file .\download.ps1` (if it needs to be a single file, it will get more difficult). – iRon Aug 12 '22 at 12:46
  • Error: download.ps1 cannot be loaded because running scripts is disabled on this system... Omg how hard it is to download files on windows... – stkvtflw Aug 15 '22 at 07:01
  • See: [PowerShell says "execution of scripts is disabled on this system.](https://stackoverflow.com/q/4037939/1701026) – iRon Aug 15 '22 at 09:35
  • I have updated my answer with (hopefully) the final steps to make a batch file that is easy to launch for novice users. – iRon Aug 16 '22 at 06:39
  • That helped! I made the batch file to download the ps file, execute it and delete it. This way it kinda fits "one double-clicked file". I'm hoping to get an alternative solution. Otherwise I'll accept your answer as the best one. Big thanks you for your input! – stkvtflw Aug 16 '22 at 07:04
  • I have also added a plain command version to the answer that might work: `powershell -command "Get-Content .\urls.txt |ForEach-Object { $FileName = Split-Path -leaf $_; Invoke-WebRequest $_ -OutFile $FileName }"` (see my notes it the changed answer). – iRon Aug 16 '22 at 07:09