こんにちは!GAS(Google Apps Script)を使ってAPI連携やツール開発の学習をしているblueです。
GeminiなどのAIのAPIを使っていると、「テキストのやり取りはできるようになったから、次は画像や音声ファイルを送って解析させたい!」と思うこと、ありませんか?
今回は、技術書典でも大人気のfreeeloverさんの書籍(Google Apps Script AI組み込みプログラミング)を基に勉強した、「GASからAPIへファイルを送る仕組み」を図解とコード付きで分かりやすく解説します!
なぜファイルを「そのまま」送れないの?
最大の壁は、「インターネット(API)の世界は、基本的に『文字(テキスト)』しか通れない」という点にあります。
画像や音声のファイルをそのまま送ろうとしても、APIは受け取ってくれません。
そこで、ファイルを「文字の暗号」に変身させる必要があります。
その変身の3ステップを見ていきます。
ステップ1:写真が「0と1」のデータになる

私たちが普段見ている写真は、パソコンの中ではピクセルごとの色の強さ(RGBなど)を数値化したデータの集まりです。
これを一番下まで分解していくと、最終的には「0と1」が果てしなく続くデジタルデータ(バイナリデータ)になります。
ステップ2:バラバラのデータをひとまとめの「Blob」にする

むき出しの「0と1」のデータのままだと、プログラムは「どこからどこまでが1つの写真なの?」と困ってしまいます。
そこで、このデータをひとまとめにして、持ち運びやすい「魔法の袋」に入れます。
この袋のことをプログラミングの世界では「Blob(ブロブ)」と呼びます。
とりあえずBlobにしてしまえば、GASの中で「1つのファイル」として扱いやすくなります。
ステップ3:文字列の暗号「Base64」に変換する

APIは「文字」しか受け取れません。
そこで、袋(Blob)に入ったデータを変換機にかけ、英数字と記号(A-Z, a-z, 0-9, +, /)だけで構成された「長い文字列(テキストデータ)」に変換します。
この文字化する仕組みを「Base64 Encode(ベース64・エンコード)」と呼びます。
これでようやく、API宛ての注文書(JSON)に貼り付けて送れるようになります!
実践!GASのコードで見てみよう
仕組みが分かったところで、実際にfreeeloverさんの書籍を参考にしたGASのコードを見てみましょう。
「画像」を送る場合と「音声」を送る場合で、袋に貼る「名札(ファイルの種類)」の扱い方が少し違うのがポイントです!
パターン1:画像ファイルを送るコード(MimeTypeを使う)
画像のURLからデータを取得し、Gemini APIに送るコードです
function analyzeImageFromUrl() {
// === 事前準備:ご自身の環境に合わせて変更してください ===
const GEMINI_API_KEY = 'ここにあなたのGemini APIキーを入力してください';
// OpenAI互換エンドポイントを使用する場合のURL
const GEMINI_CHAT_URL = 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions';
const GEMINI_MODEL = 'gemini-2.5-flash';
// 解析対象の画像URL(ご自身の好きな画像URLに変更してください)
const imageUrl = 'https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png';
// =================================
// AIの役割(システムロール)とお願い(プロンプト)を指定
const systemRole = 'あなたはプロの画像アナリストです。与えられた画像を正確に分析し、分かりやすく解説してください。';
const prompt = 'この画像に写っている主な被写体と、その特徴を3つ教えてください。';
// ①画像のURLからBlob(袋)オブジェクトを取得する
const blob = UrlFetchApp.fetch(imageUrl).getBlob();
// ②袋から公式名札(MimeType)を取得する
const mimeType = blob.getContentType();
// ③文字列(Base64)に変換する
const base64Image = Utilities.base64Encode(blob.getBytes());
// ④Data URIスキームという「名札+暗号文字」のセットを作る
const dataUrl = `data:${mimeType};base64,${base64Image}`;
const content = [
{ type: 'text', text: prompt },
{ type: 'image_url', image_url: { url: dataUrl } } // ここにセット!
];
const messages = [
{ role: 'system', content: systemRole },
{ role: 'user', content: content }
];
const payload = {
model: GEMINI_MODEL,
messages: messages,
};
const params = {
contentType: 'application/json',
headers: { Authorization: `Bearer ${GEMINI_API_KEY}` },
payload: JSON.stringify(payload),
muteHttpExceptions: true // エラーの詳細を確認しやすくするためにtrueに設定
};
// APIへ送信して結果を受け取る
try {
const responseBody = UrlFetchApp.fetch(GEMINI_CHAT_URL, params).getContentText();
const chat = JSON.parse(responseBody);
if (chat.error) {
console.log("エラーが発生しました: " + chat.error.message);
return;
}
const answer = chat.choices[0].message.content;
console.log("【Geminiの回答】\n" + answer);
} catch(e) {
console.log("通信エラー: " + e.message);
}
}
【解説ポイント】 画像の場合は、blob.getContentType() を使って、袋についている公式の取扱説明書(例:image/jpeg)を読み取っています。
これをBase64の文字列とくっつけてAPIに渡します。
うまくいくと以下のログが返ってきます。

パターン2:音声ファイルを送るコード(拡張子を使う)
次に、Googleドライブに保存されている音声ファイルをGemini APIに送るコードです。
function analyzeAudioFromUrl() {
// === 事前準備:ご自身の環境に合わせて変更してください ===
const GEMINI_API_KEY = 'ここにあなたのGemini APIキーを入力してください';
const GEMINI_CHAT_URL = 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions';
const GEMINI_MODEL = 'gemini-2.5-flash';
// 【修正済み】テスト用のフリー音声URL(Mozilla MDN公式のサンプル音声)
const audioUrl = 'https://dpgr.am/spacewalk.wav';
// =================================
// 【修正済み】AIのテストによく使われる、超安定したフリー音声(宇宙飛行士の交信音声)
const systemRole = 'あなたは優秀な通訳・文字起こしアシスタントです。提供された音声を正確に聞き取り、丁寧な言葉遣いで対応します。';
const prompt = 'この音声データで話されている内容を文字起こしし、さらにそれを日本語に翻訳して教えてください。';
// ①指定したURLから音声データを取得し、Blob(袋)にする
const blob = UrlFetchApp.fetch(audioUrl).getBlob();
// ③文字列(Base64)に変換する
const base64Audio = Utilities.base64Encode(blob.getBytes());
// ②URLの末尾から短い名札(拡張子: mp3など)を切り取る!
const fileName = audioUrl.split('?')[0];
const audioFormat = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
// 音声ファイルをGemini APIへ送信する準備
const content = [
{ type: 'text', text: prompt },
{
type: 'input_audio',
input_audio: {
data: base64Audio,
format: audioFormat // ここに「mp3」などの短い名札をセット!
}
}
];
const messages = [
{ role: 'system', content: systemRole },
{ role: 'user', content: content }
];
const payload = {
model: GEMINI_MODEL,
messages: messages,
};
const params = {
contentType: 'application/json',
headers: { Authorization: `Bearer ${GEMINI_API_KEY}` },
payload: JSON.stringify(payload),
muteHttpExceptions: true // エラーの詳細を確認しやすくするためにtrueに設定
};
// APIへ送信して結果を受け取る
try {
const responseBody = UrlFetchApp.fetch(GEMINI_CHAT_URL, params).getContentText();
const chat = JSON.parse(responseBody);
if (chat.error) {
console.log("エラーが発生しました: " + chat.error.message);
return;
}
const answer = chat.choices[0].message.content;
console.log("【Geminiの回答】\n" + answer);
} catch(e) {
console.log("通信エラー: " + e.message);
}
}
【解説ポイント】 今回の音声のAPI仕様では、audio/mpeg のような長い公式名札ではなく、mp3 や wav といった短い名前(フォーマット)を注文書(format)に書く必要があります。
そのため、blob.getName() でファイル名を取得し、そこから拡張子を切り取るという工夫をしています。
うまくいくと以下のログが返ってきます。

まとめ
APIにファイルを送りたい時は、以下の魔法の変身ステップを思い出してください!
- Blob(魔法の袋)にまとめる
- ファイルの種類(名札)を確認する
- Base64(文字の暗号)に変換して送信!
この仕組みさえ理解してしまえば、PDFでも動画でも、どんなファイルでもGASから自由自在にAPIへ送れるようになります。
ぜひご自身のコードでも試してみてくださいね!

コメント