Question is in the title. I don’t know if the answer feels logical to most, but it wasn’t to me when I first thought about it. Because:
- You can download a finite file generated by a server.
- You can try to download an infinite file generated by a server.
So if you’re here for a short answer to the title: You can’t.
I initially got the idea to try to answer this question when I first saw OVH’s promotion on a few domain names. “.download” was only 0.50€ for a year, and thought it was worth the time to try to make a joke page with an infinitely downloading file (actually streaming content, not just a frozen download).
So I bought “cantstopthe.download”. The goal was to set up a very simple HTML page, with a link to start the download of that file. But I don’t want to generate useless network traffic ; the download should be entirely happening on the client’s computer.
Infinite download from a server
Let’s start by remembering how easy it is to make this to work with a server. The trick of course is not to store an infinitely large file and serve it, but to generate it as the user is downloading.
This isn’t even remotely a challenge : Just provide a link to download a file that executes an infinite loop when queried. Here’s a trivial example of this in PHP :
If you try to get the
infinite.php file, you’ll receive endless “Hi.” until you manually stop the download.
Now, things get more interesting when you want to delete the server part.
I see two main ways to achieve the download of a file from an user’s own browser : Either build a File from a Blob and link to it, or use a Data URI to represent the file.
Take 1 : Link to a built file
We can use the
Blob Web API to create a representation of a file. So first, we’ll create a finite text File to download using a
Blob, and we’ll provide a link pointing to its created content.
What represents the file here is the blob, which is not executable code. So for our file to be downloading infinitely, we would need an infinite
Blob. We can of course create a very long string with a loop that we would then assign to the
Blob. But we would be trapped in that loop that creates the string, not in the download of it. So it seems like we’re executing code a bit too far from our target to be able to make the file infinitely download.
Take 2 : Data URI
Using a Data URI is not that much different from the final result we obtained using the blob. We want to generate an infinitely large Data URI that would represent our file. But this time, we don’t need to assign it to an other variable (as we did with the
Blob). So we can try to build the file directly with the string, without having to keep it in memory.
We could try to generate the infinitely large URI in a loop, but we would be stuck in that loop the same way we talked about before.
The next step is to build the URI at the time the user tries to access it. We’ll need to evaluate the value as late as possible.
Lazy evaluation ?
An idea would be to evaluate the string lazily, at the moment it’s asked for. We’ll need to change the code a bit. We don’t want to assign a value to the
href property, because we’d have to build the infinite string that before the user clicks the link, which we saw is impossible. Instead, we’d like to build the data as the user requests it.
Here is an other example of a script that downloads a file.
In this example, I’m using
data:Application/octet-stream so that browsers won’t know how to interpret the incoming data, and will try to download the incoming file as a result.
infinite_string_should_be_here string ? We can now try to replace this with a lazy string, right ?
The library Stream.js gives us a way to manipulate objects that behave like streams and generators. Which is what we want, right ? We’ll just have to make a stream that generates an infinite amount of characters.
Here’s what our code could be resembling…
The tab freezes : All of this for an infinite loop, and not the one we wanted. Maybe you saw it coming when
true is sitting just next to
The explanation is trivial : Even with tons of laziness, the right-side of the expression has to be fully evaluated before being assigned. The end will of course never happen here, leaving the interpreter collect as many
Hi. as it can handle until the tab crashes. And so ends my attempts.
What would it take to actually be able to achieve this ? The problem here is that whatever we do, we’ll need to evaluate the full content to be downloaded before being able to start. I can’t see any way we could detach the process generating the data from the process trying to get it for now.
I tried to answer this question at first because I fount it amusing and I didn’t really got any satisfying answer after googling a bit. After having found my answer, I’d say this was a fun small research for me. :)