fix(web): download archives via html POST forms (#30021)

* fix: download archives via html POST forms

* refactor: fix css of download panel, add translations and toasts

* style: run lint, formatter, check

* chore: generate open-api specs

* refactor(web): simplify download manager, use SvelteMap

* refactor(server): parse fields from x-www-form-urlencoded request

* refactor: move content-disposition to ImmichReadStream

* Pass asset ids as a comma-separated string

This fixes a bug where the 1000 parameter limit is being hit when
downloading 1000+ assets.

* Add download controller test

* Fix lint warnings
This commit is contained in:
Diogo Correia
2026-08-20 20:58:41 -04:00
committed by GitHub
parent c7531a36f9
commit c9776fb990
18 changed files with 178 additions and 91 deletions
+26 -4
View File
@@ -24,7 +24,6 @@ import { init, register, t } from 'svelte-i18n';
import { derived, get } from 'svelte/store';
import { defaultLang, locales } from '$lib/constants';
import { authManager } from '$lib/managers/auth-manager.svelte';
import { downloadManager } from '$lib/managers/download-manager.svelte';
import { alwaysLoadOriginalFile, lang } from '$lib/stores/preferences.store';
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { handleError } from '$lib/utils/handle-error';
@@ -293,15 +292,38 @@ export const downloadUrl = (url: string, filename: string) => {
URL.revokeObjectURL(url);
};
export const downloadUrlPost = (url: string, assetIds: string[], archiveName: string) => {
const form = document.createElement('form');
form.method = 'post';
form.action = url;
form.target = '_blank';
function mkInput(name: string, value: string) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
input.value = value;
form.append(input);
}
mkInput('assetIds', assetIds.join(','));
mkInput('archiveName', archiveName);
mkInput('edited', 'true');
document.body.append(form);
form.submit();
form.remove();
};
export const downloadBlob = (data: Blob, filename: string) => downloadUrl(URL.createObjectURL(data), filename);
export const downloadJson = (data: unknown, filename: string) => {
const blob = new Blob([JSON.stringify(data, jsonReplacer, 2)], { type: 'application/json' });
const downloadKey = filename;
downloadManager.add(downloadKey, blob.size);
downloadManager.update(downloadKey, blob.size);
downloadBlob(blob, downloadKey);
setTimeout(() => downloadManager.clear(downloadKey), 5000);
const $t = get(t);
toastManager.info($t('downloading_filename', { values: { filename } }));
};
export const oauth = {