HTML, JavaScript: zapis obrazu <img> na dysk, Canvas, File System Access API, CORS

Pytanie

html, jak zapisać obraz &amp;lt;img&amp;gt; do pliku na dysku

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Sam HTML nie potrafi zapisywać plików.
  • W praktyce stosuje się JavaScript (np. atrybut download, Canvas API + toBlob, File System Access API) albo zapis po stronie serwera (PHP, Node.js, …).

Kluczowe punkty
<a download> – najszybsze, ale wymaga tego samego pochodzenia (Same-Origin).
• Canvas API → toBlob() / toDataURL() – pozwala pobrać dowolny obraz renderowany w przeglądarce, o ile nie blokuje CORS.
• File System Access API – najnowszy sposób na bezpośredni zapis pliku na dysku (Chromium ≥ 86).
• Zewnętrzne domeny ⇒ sprawdzić nagłówki CORS lub użyć proxy po stronie serwera.


Szczegółowa analiza problemu

1. Same ograniczenia przeglądarki

Przeglądarki chronią system plików użytkownika:
• żaden skrypt nie może zapisać pliku w tle;
• użytkownik musi zainicjować lub zatwierdzić operację (kliknięcie, okno wyboru katalogu).

2. Metody po stronie klienta (JavaScript)

Metoda Kod (skrót) Plusy Minusy Kiedy wybrać
&lt;a download> <a href="img.jpg" download="nazwa.jpg">Pobierz</a> 1 linijka, brak JS Tylko same-origin; brak kontroli nad nazwą/formatem poza download Gdy obraz i strona są na tym samym serwerze
Canvas API patrz kod A Działa dla <img>, <video>, SVG; można zmienić format, kompresję CORS; duże obrazy ⇒ pamięć Gdy trzeba modyfikować lub konwertować obraz
fetch → Blob → URL.createObjectURL kod B Pobiera dowolny plik (PNG/JPG/WebP/…); nie wymaga Canvas CORS; brak modyfikacji Gdy zależy na oryginalnych danych binarnych
File System Access API kod C Bezpośredni zapis do wskazanego przez użytk. katalogu Chrome/Edge/Opera; wymaga HTTPS i user gesture Aplikacje PWA, edytory online
Biblioteka FileSaver.js saveAs(blob, 'nazwa.png') Cross-browser (IE 10+); ukrywa różnice Dodatkowa zależność Szybki fallback dla starszych przeglądarek

Kod A – Canvas + JPEG 80 %

async function saveImgCanvas(id, fname='obraz.jpg', quality=0.8){
  const img = document.getElementById(id);
  const canvas = document.createElement('canvas');
  canvas.width  = img.naturalWidth;
  canvas.height = img.naturalHeight;
  canvas.getContext('2d').drawImage(img,0,0);
  const blob = await new Promise(r => canvas.toBlob(r,'image/jpeg',quality));
  const url  = URL.createObjectURL(blob);
  triggerDownload(url, fname);
}
function triggerDownload(url, fname){
  const a = Object.assign(document.createElement('a'), {href:url, download:fname});
  document.body.append(a); a.click(); a.remove();
  URL.revokeObjectURL(url);
}

Kod B – fetch → Blob

async function downloadFromSrc(src,fname='plik'){
  const res = await fetch(src,{mode:'cors'});
  if(!res.ok) throw new Error(res.status);
  const blob = await res.blob();
  triggerDownload(URL.createObjectURL(blob), fname);
}

Kod C – File System Access (Chromium)

async function saveWithFSA(imgId){
  const img = document.getElementById(imgId);
  const handle = await window.showSaveFilePicker({suggestedName:'obraz.png',
      types:[{description:'PNG',accept:{'image/png':['.png']}}]});
  const writable = await handle.createWritable();
  const blob = await (await fetch(img.src)).blob();
  await writable.write(blob);
  await writable.close();
}

3. Metody po stronie serwera

Jeżeli:
• obraz jest z innej domeny bez CORS,
• trzeba go zapisać „w tle”,
przekazujemy URL do backendu i tam zapisujemy go na dysk:

PHP

file_put_contents('obrazy/'.basename($url), file_get_contents($url));

Node.js (HTTPS)

https.get(url,res=>res.pipe(fs.createWriteStream('img.jpg')));

Aktualne informacje i trendy

• File System Access API staje się standardem w PWA – umożliwia desktop-like workflow w przeglądarce.
• WebCodecs i WebGPU generują obrazy/ramki, które często zapisuje się właśnie przez Canvas → Blob lub FS API.
• Format WebP/AVIF zyskuje na popularności; Canvas toBlob('image/avif') ma już wstępne wsparcie w Chrome 121+.
• Coraz częściej aplikacje frontendowe korzystają z WebAssembly (np. libvips/wasm) do edycji przed zapisem.


Wspierające wyjaśnienia i detale

• Same-Origin Policy – przeglądarka pozwala odczytać piksele tylko, jeśli protocol + domain + port są identyczne lub header Access-Control-Allow-Origin zezwala.
canvas.toDataURL() zwraca Base64; toBlob() zwraca binarne dane – lepsze dla dużych plików (mniej RAM).
URL.revokeObjectURL() – konieczne, by zwolnić pamięć.
• FileSaver.js pod IE używa msSaveOrOpenBlob.


Aspekty etyczne i prawne

• Prawa autorskie – pobieranie obrazów chronionych bez licencji jest nielegalne.
• RODO/CPRA – jeśli obraz zawiera dane osobowe (np. twarze), jego pobieranie lub przechowywanie wymaga podstawy prawnej.
• Bezpieczeństwo – unikać zapisywania w stałych lokalizacjach; FS API zawsze wymusza wybór katalogu przez użytkownika.


Praktyczne wskazówki

  1. Zawsze zaczynaj od najprostszego (<a download>).
  2. Sprawdź CORS na obrazach zewnętrznych (curl -I URL).
  3. Przy dużych plikach stosuj toBlob() zamiast toDataURL().
  4. W aplikacjach PWA włącz display: standalone, by FS API działało płynnie.
  5. Testuj w Chrome, Firefox i Safari; Safari 16 + obsługuje download, ale nie FS API.

Ewentualne zastrzeżenia lub uwagi dodatkowe

• FS API nie jest jeszcze standardem W3C; w Safari/Firefox dostępne wyłącznie za flagami.
• Stare przeglądarki (IE ≤ 9) – brak download, konieczny BlobBuilder lub serwer.
• Przy obrazach z kamer (getUserMedia) potrzebny jest HTTPS.


Sugestie dalszych badań

• Monitoruj specyfikację File System Access (WICG).
• Sprawdź projekt Native File System Adapter w Firefox Nightly.
• Eksperymentuj z WebAssembly (Squoosh, Sharp/wasm) w celu bezstratnej kompresji przed zapisem.
• Rozważ Service Workers do buforowania i „offline save”.


Krótkie podsumowanie

Sam HTML nie zapisze pliku. Najłatwiej dodać link z atrybutem download. Jeżeli potrzebna jest manipulacja obrazem – renderujemy go na Canvas i zapisujemy przez toBlob(). Dla aplikacji przypominających desktop można wykorzystać File System Access API, a w sytuacjach zablokowanych przez CORS – pobrać obraz na serwer i tam go zapisać. We wszystkich przypadkach konieczna jest świadoma interakcja użytkownika oraz respektowanie praw autorskich.

Oceń odpowiedź:

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (2min)...
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.