Cloudflare Workersの勉強中です。LINEに送った写真をR2にアップロードする処理を書いてみます。
wrangler v3.22.4
で試してます。
LINEアルバムに写真は載るけど動画は載らない
という、LINEの仕様問題があるのでグループに上げた動画をR2に保存する仕組みみたいなものを作りたいなと思いました。
(ちなみに最近はLYPプレミアムに加入すると動画アルバムも作れるぽいです。 https://premium.yahoo.co.jp/ )
ファイルシステムが無い
Cloudflare Workersだとファイルシステムが無いのでローカルに保存後にアップロードみたいなことができません。
LINEのAPIにアクセス -> DL -> ArrayBufferに詰める -> R2に上げる といった流れでやってみました。
準備 - LINEから写真を受け取る
まずはオウム返し的なLINE Botを作りましょう。(記事は特に今の所ないですが)
Fetch APIでファイルをダウンロード
Cloudflare Workers上だとLINEのNode.js SDKはおそらく使えないので、Fetch APIでDLする方法を調べます。
const res = await fetch("https://example.com/path/to/image.png");
const arrayBuffer = await res.arrayBuffer();
こんな感じで受け取れるみたいですね。
参考:
LINE Botに送信された写真をダウンロード
LINE Messaging APIのhttps://api-data.line.me/v2/bot/message/(messageId)/content
のエンドポイントからDLできます。
DLするファイルが動画なのか画像なのかなども知りたいですが、res.headers
で調べられそうでした。
const API_ENDPOINT = `https://api-data.line.me/v2/bot/message/${event.message.id}/content`
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.CH_ACCESS_TOKEN}`,
},
};
const res = await fetch(API_ENDPOINT, options);
const arrayBuffer = await res.arrayBuffer(); //ダウンロードしてArrayBufferに格納
//扱うファイルのタイプを取得 'video/mp4'だったり'image/jpeg'だったり
const headers = res.headers;
const filetype = headers.get('content-type');
ファイルをR2にアップロード
こちらが参考になりました。
こんな感じでアップロード(put)できます。
const bucket = env.NOBISUKE_BUCKET;
const response = await bucket.put(filename, arrayBuffer, {
httpMetadata: {
contentType: filetype,
}
});
まとめる - LINE Messagin APIから写真をDLしてR2にアップロードまで
まとめるとこんな感じでできました。
//写真や動画のDL
const API_ENDPOINT = `https://api-data.line.me/v2/bot/message/${event.message.id}/content`
const options = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${env.CH_ACCESS_TOKEN}`,
},
};
const res = await fetch(API_ENDPOINT, options);
const arrayBuffer = await res.arrayBuffer();
//ファイルタイプ(MIMEタイプ)を取得
const headers = res.headers;
const filetype = headers.get('content-type');
console.log(filetype);
//ファイル名をLINEのMessageIDから生成
let filename = '';
if(filetype === 'video/mp4'){
//filename = `${groupId}/${event.message.id}.mp4`; //GroupIDなどをフォルダ名にしたりも
filename = `${event.message.id}.mp4`;
}else if(filetype === 'image/jpeg'){
filename = `${event.message.id}.jpg`;
}
//R2のバケットにアップロード
const bucket = env.NOBISUKE_BUCKET;
const response = await bucket.put(filename, arrayBuffer, {
httpMetadata: {
contentType: filetype,
}
});
console.log(`---R2にput成功...`)
console.log(response, response.key);
これで写真や動画がR2にアップロードされます。 だがしかし。
試してみて
たまにキャンセルされる
Cloudflare Workersだとリクエストに対してCPUの時間が10msという制限がある模様なので、おそらく重たいファイルなどを扱うときにここで詰まるのかなと思いました。
僕が試した時は
- iPhoneで撮った写真はほぼ問題ないけど、2割くらいこけてた
- 8秒くらいの動画は短い動画は保存されて、30秒以上くらいの動画は全てこけてた
って感じでした。
まとめ
ちょっと挙動的にどこに問題があるのか分かって無いですが、軽いサイズのファイルだとこれで問題なさそうです。
そもそも動画はCloudflare Streamを使うのが良いのか?など調べつつでしたが無料スタートがなさそうだったので一旦こんなところでまとめました。
たまにキャンセルされる問題はこうすると良いよって話があれば教えてもらえると嬉しいです。