Иногда передо мной стоит задача сделать ссылку, которая должна открывать системный диалог для сохранения файла. Браузеры достаточно умны, чтобы открывать такой диалог при скачивании бинарников вроде архивов или EXE-файлов. Но что делать, если я хочу скачивать картинки или какие-нибудь видео? Именно скачивать, не открывать для просмотра.
Заголовок Content-Disposition Скопировать ссылку
Самый правильный способ попросить браузер скачать файл — добавить на стороне сервера заголовок Content-Disposition
к потоку с файлом.
Content-Disposition: attachment; filename=kitten.jpg
Когда браузер видит у заголовка значение attachment
, то пытается скачать файл.
Но иногда у вас просто нет возможности настроить сервер под свои нужды и добавить ещё один mod_rewrite
. Нужен какой-то более браузерный способ решить задачу.
Атрибут download Скопировать ссылку
Самый простой способ — добавить атрибут download
к ссылке.
Если вы добавите его просто так, без значения, браузер постарается получить имя файла либо из заголовка Content-Disposition
(опять он, и у него довольно высокий приоритет), либо из пути файла.
<a href="images/kitten.jpg" download>
<img src="images/kitten-preview.jpg" alt="Котёнок, превью.">
</a>
Попробуйте: ссылка.
Но вы можете задать значение атрибуту download
, и тогда это значение станет именем скачиваемого файла. Это может быть полезно, если у ваших файлов какие-нибудь странные автогенерируемые урлы вроде https://cdn/images/a1H5-st42-Av1f-rUles
.
<a href="images/1h24v9lj.jpg" download="kitten">
Скачать
</a>
Попробуйте: ссылка.
Важно! Вся эта магия атрибутов не для ссылок с других доменов (cross-origin). Вы не можете управлять чужими ресурсами из соображений безопасности.
И помните, что IE и старые Safari не понимают атрибут download
. Проверьте в Can I use….
blob: и data: Скопировать ссылку
Полезный лайфхак, чтобы помочь вашим пользователям сохранять картинки котиков в удобном для них формате. Если вы на своём сайте используете картинки в форматах AVIF или WebP, есть очень высокая вероятность, что ни один пользователь не сможет сохранить их к себе на компьютер или смартфон, чтобы потом пересмотреть. Точнее, сохранить-то смогут, а вот посмотреть вне браузера не смогут. Печаль.
Чтобы помочь пользователям, используйте data:
или blob:
внутри атрибута href
.
Шаг 1. Нарисуйте картинку на Canvas Скопировать ссылку
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const image = new Image();
image.onload = function () {
context.drawImage(image, 0, 0);
// TODO: всю магию намазывать сюда
};
image.src = 'kitten-170.avif';
Шаг 2а. Сохранить картинку как блоб в атрибут href
ссылки Скопировать ссылку
const blobLink = document.getElementById('blob-link');
canvas.toBlob(blob => {
const blobUrl = URL.createObjectURL(blob);
blobLink.href = blobUrl;
}, 'image/jpeg', 0.9);
Да-да, я могу сохранить AVIF как JPEG. Классно, правда? Пользователь скачал всего 4 КБ AVIF с сервера, а получил 13 КБ JPEG на клиенте!
Шаг 2б. Сохранить картинку как data в атрибут href
ссылки. Скопировать ссылку
Некоторые браузеры не умеют работать с блобами, поэтому вы можете помочь им при помощи data-урлов.
const dataLink = document.getElementById('data-link');
dataLink.href = canvas.toDataURL('image/jpeg', 0.9);
Так даже проще, но такой подход хуже по производительности.
Можете поиграть с полным демо тут: