LINEBotに送った画像をGoogleDriveに保存して共有URLを返すまで


はじめに

GoogleDriveAPIを触っていたので、LINEBotに送信した画像を代わりにアップロードし、共有URLを返して欲しくなったので実装してみました。

どうなりたいか

単純に画像URLが欲しい時に使う感じです。(パソコンだとなんとでもなるけど、スマホ単体の時には結構助かる)

仕組み

①画像を受け取るLINEBotを実装する

LINEBotでユーザーから画像メッセージを受け取ると以下のようなJSONが送られてきます。

 {
  "type": "message",
  "replyToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "source": { "userId": "123456789abcdefghijk", "type": "user" },
  "timestamp": 1611364562326,
  "mode": "active",
  "message": {
    "type": "image",
    "id": "13429433729723",
    "contentProvider": {
      "type": "line"
    }
  }
}

LINE Messaging APIでは直接画像データが送られてくる訳ではないので注意が必要です。
JSONの中のmessageプロパティの中にあるidを使ってコンテンツ取得のAPIを叩くことでデータを取得することができます。
※画像の他にも動画や音声ファイルなども同じようにidが送られてきます。
詳しくは公式のリファレンスのこちらをご覧ください。

②メッセージIDから画像データを取得する

LINE Messaging APIにはユーザーが送ってきたデータをコンテンツ取得のAPIを利用して扱うことができます。詳しくは公式リファレンスはのこちらをご覧ください。

nodejsだと以下のように利用できます(公式リファレンス引用)

index.js
const line = require('@line/bot-sdk');

const client = new line.Client({
  channelAccessToken: '<channel access token>'
});

client.getMessageContent('<messageId>')
  .then((stream) => {
    stream.on('data', (chunk) => {
      ...
    });
    stream.on('error', (err) => {
      // error handling
    });
  });

③サービスアカウントを作成する

まず、サービスアカウントというものを作成しなければいけません。
LambdaからgoogleDriveAPIを操作するこちらの記事の通りにサービスアカウントを作成し、連携することで、google APIを利用することができるようになります。

④サービスアカウントにGoogle Driveのファイルを共有する

画像をアップロードする権限を付与しなければいけません。
サービスアカウントを作成したときにダウンロードするJSONの中にあった、client_emailのメールアドレスをコピーし、画像をアップロードしたいDriveのファイルをそのメールアドレスに共有します。
▼GoogleDriveでフォルダ共有の方法


⑤フォルダIDを取得する

先ほどサービスアカウントに共有したフォルダを開き、そのフォルダのIDを取得します。

https://drive.google.com/drive/u/1/folders/xxxxxxxxxxxxxxxxxxxxxxxx

フォルダを開いた状態の時のurlを確認し、/folders以降のxxxxxxxxxxxxxxxxxxxxxxxxをコピーします。

⑥Google APIでGoogle Driveにアップロードする

画像をアップロードする部分は以下のように書くことができます。

index.js
const drive = google.drive({ version: "v3", auth: jwtClient });

//アップロードする画像のデータを取得
const imageStream = await client.getMessageContent(event.message.id);

var fileMetadata = {
  //nameはDriveに保存する時のファイル名になります
  name: `photo.jpg`,
  parents: ["<先ほど取得したフォルダID>"],
};
var media = {
  mimeType: "image/jpeg",
  body: imageStream,
};
await drive.files.create(
  {
    resource: fileMetadata,
    media: media,
    fields: "id",
  },
  function (err, file) {
    if (err) {
      // Handle error
      console.error(err);
    } else {
      //file.data.idをアップロードされたファイルID
      resolve(file.data.id);
    }
  }
);

⑦返ってきたファイルIDを元に画像URLを作る

https://drive.google.com/ucにパラメータとして先ほど取得したファイルIDをつけることで画像URLが完成します。

ex)
https://drive.google.com/uc?id=1T4QABnxnU_gNt6ORxNvnKU9EiiFxn795

⑧応答するメッセージを作成(リファレンス引用)

応答メッセージの記述も公式リファレンスを参考にしながら書くことができます。こちら

index.js
const line = require('@line/bot-sdk');

const client = new line.Client({
  channelAccessToken: '<channel access token>'
});

const message = {
  type: 'text',
  text: '保存に成功したよ!https://drive.google.com/uc?id=1T4QABnxnU_gNt6ORxNvnKU9EiiFxn795'
};

client.replyMessage('<replyToken>', message)
  .then(() => {
    ...
  })
  .catch((err) => {
    // error handling
  });

▼成功したらこんな感じ

 

最後に

はじめてGoogle Drive APIを触ったついでに作ってみたのですが、やっぱり何かと画像URLを作成してくれるのは楽ですよね。PCがあればそれほど必要ないかもしれないですが、何かと役に立ちますよ。是非皆さんも挑戦してみてください。お疲れ様でした。

▼ちなみにGoogleDriveの反映スピード半端ないです。