Link to download
Sometimes I need to create a link that should show a system dialog to save the file. Browsers are pretty smart to open that dialog for some binaries, e.g. for archives or *.exe
. But what if I want to download an image or some video?
Content-Disposition
header
The most valid way is to add a Content-Disposition
header on the server.
Content-Disposition: attachment; filename=kitten.jpg
When your browser finds the attachment
value, it starts to download the file, not to show it.
But sometimes you can’t change any server configs, so we need a more browserish way to solve the problem.
download
attribute
The easiest way to do this is to add the download
attribute to your link.
If you just add it without any value, your browser will try to get the name of the file from its Content-Disposition
header (yeah, it has high priority) or its path.
<a href="images/kitten.jpg" download>
<img src="images/kitten-preview.jpg" alt="Kitten photo preview">
</a>
Try it: demo link.
You can set some value to download
if you want to change the default name. It can help when you have some strange auto-generated URL like https://cdn/images/a1H5-st42-Av1f-rUles
.
<a href="images/1h24v9lj.jpg" download="kitten">
Download
</a>
Try it: demo link.
Important! All this attribute magic is not for cross-origin links. You can’t control the stuff from cross-origin sources due to security issues.
And remember that IE and old Safari are unaware of download
. Check it.
blob:
and data:
Another lifehack to help your users save pictures of kittens in a format convenient for them. If you use AVIF or WebP images on your site, it is a huge chance that no one will be able to save them on their computer or phone to view them later. More precisely, they can save the image, but they can’t view it. Very sad.
To help them, use data:
or blob:
URLs inside the href
attribute.
Step 1. Draw the image on a canvas.
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const image = new Image();
image.onload = function () {
ctx.drawImage(image, 0, 0);
// TODO: put the magic here
};
image.src = 'kitten-170.avif';
Step 2a. Save the image as a blob to the link href
.
const blobLink = document.getElementById('blob-link');
canvas.toBlob(blob => {
const blobUrl = URL.createObjectURL(blob);
blobLink.href = blobUrl;
}, 'image/jpeg', 0.9);
Yes, I can save AVIF as JPEG. Cool, right? The user downloaded just 4 KB AVIF from a server and got his 13 KB JPEG on a client!
Step 2b. Save the image as a data to the link href
.
Some browsers can’t use blobs, so you can help them with data-urls.
const dataLink = document.getElementById('data-link');
dataLink.href = canvas.toDataURL('image/jpeg', 0.9);
It’s even easier, but not so performant.
You can play with the full demo here:
Sources
Webmentions [?] (likes: 19, reposts: 5)
Link to download by @dev_tip mefody.dev/chunks/downloa…
Link to download by Nikita Dubko @dark_mefody @dev_tip Create a link to download the file or save AVIF or WebP as JPEG. #ContentDisposition #downloadfiles #html #webdev #js mefody.dev/chunks/downloa…
@dark_mefody Re mefody.dev/chunks/downloa…, I just realized that in desktop Chrome and Firefox, the user can right-click on the <canvas> and select “Save image as… PNG”. Not bad.