# Obrazki z Arkuszy Google przez Apps Script

> Wiersze Arkuszy Google jako obrazki: menu w Apps Script renderuje każdy wiersz i pokazuje go przez =IMAGE, podpisany adres jako formuła i limity Google.

Opublikowano 2026-11-06 · https://shareglint.com/pl/integrations/google-sheets

Żeby generować obrazki z Arkuszy Google, dodaj do arkusza krótki skrypt **Apps Script**: polecenie w menu czyta każdy wiersz, wywołuje `https://shareglint.com/v1/images?wait=true` przez `UrlFetchApp`, wpisuje zwrócony adres obrazka do wiersza i wstawia obok `=IMAGE(…)`, żeby obrazek pokazał się w komórce. Cały arkusz naraz obsłuży też API zleceń hurtowych, które przyjmuje opublikowany arkusz jako CSV. Nie ma tu żadnego dodatku; wszystko poniżej działa w twoim własnym arkuszu.

## Sposób 1: menu do renderowania obrazków

Arkusz: kolumna A `title`, kolumna B `author`, kolumna C adres obrazka (wypełnia go skrypt), kolumna D podgląd. Otwórz **Rozszerzenia → Apps Script**, wklej skrypt i zapisz klucz API w **Ustawieniach projektu → Właściwości skryptu** jako `API_KEY` — nie w komórce, bo tę może przeczytać każdy, komu udostępniono arkusz.

```javascript
// Rozszerzenia → Apps Script. Dodaje menu Images → Render new rows.
function onOpen() {
  SpreadsheetApp.getUi().createMenu('Images')
    .addItem('Render new rows', 'renderNewRows')
    .addToUi();
}

function renderNewRows() {
  const key = PropertiesService.getScriptProperties().getProperty('API_KEY');
  const sheet = SpreadsheetApp.getActiveSheet();
  const last = sheet.getLastRow();
  if (last < 2) return;

  const rows = sheet.getRange(2, 1, last - 1, 3).getValues();
  rows.forEach(([title, author, url], i) => {
    if (!title || url) return; // pomija puste wiersze i wiersze już wyrenderowane
    const res = UrlFetchApp.fetch('https://shareglint.com/v1/images?wait=true', {
      method: 'post',
      contentType: 'application/json',
      headers: { Authorization: 'Bearer ' + key },
      payload: JSON.stringify({ template: 'blog-og', modifications: { title: String(title), author: String(author) } }),
      muteHttpExceptions: true,
    });
    const body = JSON.parse(res.getContentText());
    const row = i + 2;
    if (res.getResponseCode() !== 200) {
      // Błąd trafia do kolumny podglądu, więc przy następnym uruchomieniu wiersz zostanie ponowiony.
      sheet.getRange(row, 4).setValue('Error ' + res.getResponseCode() + ': ' + (body.error || ''));
      return;
    }
    sheet.getRange(row, 3).setValue(body.url);
    sheet.getRange(row, 4).setFormula('=IMAGE(C' + row + ')');
  });
}
```

Odśwież arkusz, wybierz **Images → Render new rows** i za pierwszym razem pozwól skryptowi łączyć się z usługą zewnętrzną. Każdy wiersz dostaje adres obrazka w kolumnie C i podgląd w kolumnie D. Wiersze, które mają już adres, są pomijane, więc po dopisaniu wierszy możesz uruchomić menu jeszcze raz; wyczyść adres, żeby wyrenderować wiersz od nowa. Te same wartości zwracają istniejący obrazek bez zużywania limitu, więc uruchomienie przerwane w połowie i powtórzone nic dodatkowo nie kosztuje.

Kody błędów lądują w kolumnie podglądu, a wiersz zostaje bez adresu, więc następne uruchomienie spróbuje go ponownie: `402` oznacza, że renderów na ten miesiąc już nie ma, `422` — błędne zapytanie (kod w `error` mówi, co jest nie tak), `503` — że obrazek nie był gotowy w czasie oczekiwania; uruchom menu jeszcze raz, a zostanie pobrany z pamięci podręcznej.

**Limity Google** ([limity Apps Script](https://developers.google.com/apps-script/guides/services/quotas), ostatnia aktualizacja 3 września 2026): uruchomienie skryptu kończy się po **6 minutach**, a wywołań URL Fetch może być najwyżej **20 000 dziennie** na koncie prywatnym i 100 000 w Google Workspace, na użytkownika. Statyczny obrazek renderuje się zwykle w kilka sekund, więc jedno uruchomienie menu obsłuży od kilkudziesięciu do kilkuset wierszy; przy większej liczbie użyj sposobu 3.

**Wyświetlanie obrazka.** `=IMAGE(url; tryb)` wstawia obrazek do komórki; tryb 1 dopasowuje go do komórki z zachowaniem proporcji, 3 zostawia oryginalny rozmiar, 4 przyjmuje własną wysokość i szerokość ([Pomoc Edytorów Dokumentów Google](https://support.google.com/docs/answer/3093333?hl=pl)). Google zastrzega: żadnych obrazów z drive.google.com i żadnego SVG — adres renderu nie jest ani jednym, ani drugim. W polskich ustawieniach regionalnych argumenty formuł rozdziela się średnikiem, stąd `;` w przykładach w tekście.

## Sposób 2: podpisany adres jako formuła

Podpisany adres obrazka oblicza się z identyfikatora konta i sekretu do podpisów, bez żadnego zapytania do API: obrazek renderuje się, gdy coś pierwszy raz pobierze ten adres. Apps Script ma wbudowane HMAC-SHA256 (`Utilities.computeHmacSha256Signature`, które zwraca tablicę bajtów — [dokumentacja](https://developers.google.com/apps-script/reference/utilities/utilities)), więc funkcja niestandardowa zbuduje adres w samym arkuszu:

```javascript
/**
 * Podpisany adres obrazka: =SIGNED_IMAGE_URL("blog-og"; "title"; A2; "author"; B2)
 * @customfunction
 */
function SIGNED_IMAGE_URL(template, ...pairs) {
  const props = PropertiesService.getScriptProperties();
  const account = props.getProperty('ACCOUNT_ID');
  const secret = props.getProperty('SIGNING_SECRET');
  // RFC 3986: encodeURIComponent nie koduje ! ' ( ) *.
  const enc = (v) => encodeURIComponent(String(v))
    .replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());

  const fields = {};
  for (let i = 0; i + 1 < pairs.length; i += 2) fields[pairs[i]] = pairs[i + 1];

  const path = '/i/' + account + '/' + template + '.png';
  const query = Object.keys(fields).sort().map((k) => enc(k) + '=' + enc(fields[k])).join('&');
  const bytes = Utilities.computeHmacSha256Signature(path + '?' + query, secret);
  const hex = bytes.map((b) => ((b + 256) % 256).toString(16).padStart(2, '0')).join('');
  return 'https://shareglint.com' + path + '?' + query + '&s=' + hex.slice(0, 32);
}
```

W komórce wpisujesz `=SIGNED_IMAGE_URL("blog-og"; "title"; A2; "author"; B2)`. Tekst kanoniczny to ścieżka, `?` i wszystkie parametry poza `s`, posortowane po nazwie i zakodowane według RFC 3986; `s` to pierwsze 32 znaki szesnastkowe HMAC — ten sam algorytm co w [dokumentacji](https://shareglint.com/pl/docs#signed). Apps Script zwraca HMAC jako bajty ze znakiem (od −128 do 127), stąd `+ 256`.

Formuły używaj tam, gdzie adres jest wykorzystywany poza arkuszem: w kolumnie, którą CMS importuje jako `og:image`, w polu korespondencji seryjnej kampanii mailowej, na liście linków dla partnera. Sekret do podpisów powinien leżeć we „Właściwościach skryptu”, jak wyżej; każdy, kto może edytować skrypt, go przeczyta, więc prawa edycji zostaw osobom, które mogą podpisywać adresy. Funkcje niestandardowe muszą zwrócić wynik w ciągu 30 sekund ([funkcje niestandardowe](https://developers.google.com/apps-script/guides/sheets/functions)) — obliczenie podpisu trwa milisekundy. Daty i liczby są podpisywane jako tekst (`String(value)`); żeby zapanować nad formatem, owiń datę w `TEXT(A2; "yyyy-mm-dd")`.

## Sposób 3: cały arkusz jako jedno zlecenie hurtowe

Przy setkach albo tysiącach wierszy obejdź się bez skryptu: opublikuj arkusz (albo jedną kartę) jako CSV i wyślij jego link do `POST /v1/batches` w polu `csv_url`. Nagłówki kolumn stają się nazwami pól, `id` i `filename` zostają przy wynikach, a jeden ZIP zawiera wszystkie obrazki. Limit wierszy na zlecenie według planu: Free 50, Starter 500, Pro 1 000, Scale 2 000, Business 5 000. Jak opublikować arkusz i jak nazwać kolumny, opisuje [poradnik o CSV i Arkuszach Google](https://shareglint.com/en/guides/bulk-og-images-from-csv-google-sheets) (po angielsku).

## Ile to kosztuje

Jeden render na obrazek i rozmiar; powtórki z tymi samymi wartościami są darmowe. Plan Free ma 100 renderów miesięcznie ze znakiem wodnym; Starter kosztuje $9 miesięcznie za 2 000 renderów ([cennik](https://shareglint.com/pl/pricing)).

## Czego nie robi

- To nie jest dodatek do Google Workspace: niczego nie instalujesz z Marketplace, jest tylko skrypt w twoim arkuszu.
- Nie czyta arkusza sam z siebie; skrypt wysyła tylko te kolumny, które ma w kodzie.
- Nie trzyma obrazków na Dysku — adres wskazuje nasz plik, przechowywany przez okres retencji twojego planu. Kopia na Dysku wymaga jeszcze jednej linii w skrypcie (`DriveApp.createFile(UrlFetchApp.fetch(url).getBlob())`).

## FAQ

### Czy Arkusze Google mogą generować obrazki z wierszy?

Tak, z krótkim skryptem Apps Script: polecenie w menu wysyła każdy wiersz do API obrazków przez UrlFetchApp, wpisuje zwrócony adres obrazka do wiersza, a formuła =IMAGE pokazuje go w komórce. Nie trzeba instalować żadnego dodatku.

### Czy obrazek może powstać z formuły zamiast z uruchomienia skryptu?

Podpisany adres obrazka możesz obliczyć funkcją niestandardową, bo Apps Script ma wbudowane HMAC-SHA256. Obrazek renderuje się przy pierwszym pobraniu adresu, co pasuje do tagów og:image i maili.

### Ile wierszy mogę wyrenderować?

Uruchomienie skryptu Apps Script kończy się po 6 minutach, a wywołań URL Fetch może być najwyżej 20 000 dziennie na koncie prywatnym (100 000 w Google Workspace). Przy setkach wierszy naraz wyślij arkusz jako jedno zlecenie hurtowe.

### Gdzie trzymać klucz API?

We „Właściwościach skryptu”, a nie w komórce, bo komórki może przeczytać każdy, komu udostępniono arkusz. Edytorzy skryptu nadal widzą jego właściwości, więc użyj klucza utworzonego tylko dla tego arkusza.
