Googleドライブの画像をApps Scriptで作ったウェブアプリで使うには

Google Apps Scriptを使うと無料でウェブアプリを開発できます。

https://qiita.com/takatama/items/9b11234ba7c420e1bf7f

この記事ではApps Scriptで作ったウェブアプリ上で、Googleドライブに保存した画像を利用する方法をいくつかご紹介します。なお非公式な方法もご紹介しますが、今後使えなくなる可能性があることにご注意ください。

| ずっと使えるか？ | Googleドライブの画像をウェブアプリで使う | 補足 |
|---|---|---|
| ずっと使える | iframe | |
| ずっと使える | Google Cloud Storageで画像をホスティング | Google Cloudが必要 |
| ずっと使える | Cloud FunctionsでDriveから画像を取得 | Google Cloudが必要 |
| ずっと使える | プロキシーサービス https://lienuc.com を使う | 有料 |
| 使えなくなった | `https://drive.google.com/uc?export=view&id={ファイルID}` | 2024年1月まで |
| 使えなくなるかも | `https://lh3.google.com/u/0/d/{ファイルid}` | |
| 使えなくなるかも | `https://drive.google.com/thumbnail?id={ファイルid}&sz=w1000` | |
| ずっと使える | Apps ScriptでデータURLを作る | 動作が遅い | 


# Googleドライブ上の画像をApps Scriptのウェブアプリで使う公式な方法

Googleが公式に認めているのは、`<iframe>`タグを使って画像を埋め込むやり方です。詳しくは

https://justin.poehnelt.com/posts/google-drive-embed-images-403/

にやり方が書いてあります。Googleドライブで画像ファイルを指定して、ケバブメニューから`Embed item...`を選ぶとHTMLのソースコードをコピーできます。

例えば、https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/view という画像の場合、`<iframe>`を使うコードは次のようになります。

```html
<iframe
  src="https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/preview"
  width="640"
  height="480"
  allow="autoplay"
></iframe>
```

しかし`<iframe>`は使い勝手がよくありません。Google Issue Trackerでのやり取りで、それ以外の方法も紹介されています。

- Google Cloud Storageで画像データをホスティングする
- Cloud FunctionsでGoogleドライブから画像データを取得する
- 他社のプロキシーサービス https://lienuc.com を利用する

https://issuetracker.google.com/issues/319531488?pli=1#comment175

# 2024年1月時点で使えなくなった非公式な方法

以前は、`<img>`タグの`src`属性に`https://drive.google.com/uc?export=view&id={ファイルID}`を指定する方法が使えていました。しかし、Googleがサードパーティ・クッキーの廃止を進めている影響で、2024年1月以降はこの方法は使えなくなっています。ウェブアプリの画面に画像は表示されず、デベロッパーツールでネットワークタブを確認すると`403 Forbidden`エラーが表示されます。

本件に関するGoogleからのアナウンスです。

https://workspaceupdates.googleblog.com/2023/10/upcoming-changes-to-third-party-cookies-in-google-drive.html

# Googleドライブ上の画像をApps Scriptのウェブアプリで使う非公式な方法

いつ使えなくなるか分かりませんが、2024年1月時点で使える方法があるのでご紹介します。次の3つのステップが必要です。

1. Googleドライブ上で画像のアクセス権限を変更する
1. 画像のファイルIDを調べる
1. ファイルIDから画像のURLを作成する

## 1. Googleドライブ上で画像のアクセス権限を変更する

公式ヘルプを参考に設定します。ウェブアプリで画像を使う場合、`ファイルを一般公開で共有する`ことが多いです。

https://support.google.com/drive/answer/2494822?hl=ja&ref_topic=7000947&sjid=6348380178211913651-AP

## 2. 画像のファイルIDを調べる

Googleドライブ上で画像を選択すると、そのURLは`https://drive.google.com/file/d/{ファイルID}/view`の形式になっています。

例えば、https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/view という画像の場合、ファイルIDは`1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu`です。

## 3. ファイルIDから画像のURLを作成する（その1）

`<img>`タグの`src`属性に`https://lh3.google.com/u/0/d/{ファイルid}`を指定することで、画像を表示できます。

https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/view という画像の場合、

```html
<img src="https://lh3.google.com/u/0/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu">
```

となります。

## 3. ファイルIDから画像のURLを作成する（その2）

`<img>`タグの`src`属性に`https://drive.google.com/thumbnail?id={ファイルid}&sz=w1000`を指定する方法も利用できます。なお、画像URLの`sz`で指定した横幅が、`<img>`タグの`width`属性より優先されるのでご注意ください。

https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/view という画像の場合、

```html
<img src="https://drive.google.com/thumbnail?id=1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu&sz=w1000">
```

となります。

# Apps ScriptでデータURLを作って表示する方法

動作速度は遅くなりますがApps ScriptでデータURLを作ってウェブアプリで表示することができます。

1. バックエンドに`fetchDataUrl(fileId)`を作る
1. フロントエンドから`fetchDataUrl(fileId)`を呼び出す（同期型と非同期型がある）

ここではデモのため、ウェブアプリのURLにQueryパラメーターとして`type=sync`があれば同期型、`type=async`があれば非同期型で動作させることにします。

```javascript:Code.gs
function doGet(e) {
  const type = e.parameter.type;
  if (type === 'sync') {
    return HtmlService.createTemplateFromFile('Sync').evaluate();
  }
  if (type === 'async') {
    return HtmlService.createHtmlOutputFromFile('Async');
  }
  return HtmlService.createHtmlOutput('Queryパラメーター type が不正です。sync もしくは async にする必要があります：' + type);
}
```

同期型と非同期型の違いは、ウェブアプリが画像を表示するまでの処理の流れにあります。ウェブアプリの用途に応じてどちらが良いか選択してください。

- **同期型（Synchronous）**では、ウェブアプリがブラウザに表示される前に、サーバー側で画像のデータURLをすべて取得してHTMLに組み込みます。この方法は、すべての画像データが準備できるまでユーザーは何も見ることができません

- **非同期型（Asynchronous）**では、ウェブアプリのHTMLを先にブラウザに表示します。その後、JavaScriptを使ってバックグラウンドで画像のデータURLを取得し、画像がダウンロードされ次第、それぞれの画像を表示します。この方法は、画像以外のコンテンツを先に見せるので、ユーザー体験が改善することがあります


## 1. バックエンドに`fetchDataUrl(fileId)`を作る

Googleドライブの`fileId`を指定して、データURLを作ります。

```javascript:Code.gs
function fetchDataUrl(fileId) {
  const blob = DriveApp.getFileById(fileId).getAs("image/webp");
  const contentType = blob.getContentType();
  const base64Data = Utilities.base64Encode(blob.getBytes());
  return `data:${contentType};base64,${base64Data}`;
}
```

## 2. フロントエンドから`fetchDataUrl(fileId)`を呼び出す（同期型）

ウェブアプリにアクセスがあると、すべての画像のデータURLを作成して画面に埋め込んでから、返信します。

```html:Sync.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Data URL（Synchronous）</h1>
    <img src="<?!= fetchDataUrl('1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu') ?>" width="480">
  </body>
</html>
```

## 2. フロントエンドから`fetchDataUrl(fileId)`を呼び出す（非同期型）

ウェブアプリにアクセスがあると、まずフロントエンドのHTMLを返信します。そして`google.script.run`を使ってバックグラウンドで`fetchDataUrl`を呼び出します。

```html:Async.html
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
  </head>
  <body>
    <h1>Data URL (Asynchronous)</h1>
    <img data-file-id="1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu" width="480">
    <script>
window.onload = function() {
  const images = document.querySelectorAll('img[data-file-id]');
  for (let image of images) {
    google.script.run.withSuccessHandler(dataUrl => {
      image.src = dataUrl;
    })
    .withFailureHandler(err => console.error(`Could not call getDataUrl: ${err}`))
    .fetchDataUrl(image.getAttribute('data-file-id'));
  }
}
    </script>
  </body>
</html>
```

## キャッシュを使った高速化が困難

https://drive.google.com/file/d/1dr139i1BCQpCFYOMTM054vuy6ih-8Dqu/view という画像の場合、生成されるデータURLの文字数は`614,699`でデータ量は約615KBです。

[CacheService](https://developers.google.com/apps-script/reference/cache?hl=ja)で保存できるデータ量は[100KBまで](https://developers.google.com/apps-script/reference/cache/cache?hl=ja#put(String,String)
)、[PropertyService](https://developers.google.com/apps-script/guides/properties?hl=ja)で保存できるのは[9KBまで](https://developers.google.com/apps-script/guides/services/quotas?hl=ja#current_limitations)（一日当りの呼び出し回数にも上限あり）なので、生成したデータURLを保存するには容量が足りません。

また、スプレッドシートの[セルの最大文字数は50,000文字まで](https://support.google.com/drive/answer/37603?hl=ja)なので、セルをキャッシュ代わりに使うこともできません。

ということで、高速化のためにはGoogle Cloud Storageで画像をホスティングするなど、別の方法を取ったほうが良さそうです。