🆙
Notion APIを使って画像をアップロードしてみました
はじめに
チームで情報をnotionに集約したいという要望があり、google spread sheetやBoxからデータを引っ張ってきてnotionにページを作る対応をしていました。
最近Notion APIで画像がアップロードできるようになったので使ってみました。
参考
こちらの記事を参考にさせていただきました、ありがとうございます。
コード
// png_upload_test.js
const { Client } = require('@notionhq/client')
const { openAsBlob } = require('node:fs')
const notion = new Client({ auth: process.argv[2] });
const fs = require('fs');
const path = require('path');
const CHUNK_SIZE = 10 * 1024 * 1024; // 10MB chunks
// 容量の小さい画像ファイルをアップロード
async function uploadSmallImage(filePath) {
const fileName = path.basename(filePath);
const contentType = 'image/png';
const stats = fs.statSync(filePath);
const fileUpload = await notion.fileUploads.create({
mode: 'single_part',
});
const send = await notion.fileUploads.send({
file_upload_id: fileUpload.id,
file: {
filename: fileName,
data: new Blob([await openAsBlob(filePath)], {
type: 'image/png',
}),
},
});
return send.id;
}
// 容量の大きい画像ファイルをアップロード
async function uploadBigImage(filePath) {
const fileName = path.basename(filePath);
const contentType = 'image/png';
const stats = fs.statSync(filePath);
const totalParts = Math.ceil(stats.size / CHUNK_SIZE);
const fileUpload = await notion.fileUploads.create({
number_of_parts: totalParts,
filename: fileName,
mode: 'multi_part',
});
const blob = await openAsBlob(filePath);
const uploadPromises = [];
for (let i = 1; i <= totalParts; i++) {
const start = (i - 1) * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, stats.size);
const chunk = blob.slice(start, end);
const promise = notion.fileUploads.send({
file_upload_id: fileUpload.id,
part_number: String(i),
file: {
filename: fileName,
data: new Blob([chunk], {
type: 'image/png',
}),
},
});
uploadPromises.push(promise);
}
await Promise.all(uploadPromises);
// multiで画像を送った場合は、sendのstatusがpendingで返ってくる
// その場合完了を待ち受けなければブロック追加時にエラーが出るのでcompleteで待つ
const complete = await notion.fileUploads.complete({
file_upload_id: fileUpload.id,
});
return complete.id;
}
// 指定したページにimageブロックを追加
async function addImage(pageId, filePath) {
const fileName = path.basename(filePath);
const contentType = 'image/png';
const stats = fs.statSync(filePath);
var sendId = "";
if (stats.size < (20 * 1024 * 1024)) {
// 20MB未満
sendId = await uploadSmallImage(filePath);
} else {
// 20MB以上
sendId = await uploadBigImage(filePath);
}
// アップロードしたファイルのsendIdを使ってimageブロックを追加
await notion.blocks.children.append({
block_id: pageId,
children: [{
type: 'image',
image: {
type: 'file_upload',
file_upload: { id: sendId }
}
}]
});
}
// メイン
async function main() {
// notionのページのID
const pageId = 'XXXXXXXXXXXXXXXXXXXX';
// 容量の小さい画像ファイルへのパス(約5MB)
const smallPngFilePath = './small.png';
// 容量の大きい画像ファイルへのパス(約24MB)
const bigPngFilePath = './big.png';
await addImage(pageId, smallPngFilePath);
await addImage(pageId, bigPngFilePath);
}
main();
処理概要
容量の小さい画像のアップロード
-
notion.fileUploads.create
で準備をします -
notion.fileUploads.send
で送信します - 戻り値のidを使って
notion.blocks.children.append
します
容量の大きい画像のアップロード
-
notion.fileUploads.create
で準備をします
ファイル名といくつに分割して送るのかの指定が必要です - 分割した数だけ、
notion.fileUploads.send
で送信します
part_numberに何個目かを指定しないといけないのですが、String型にしないとエラーがでます -
notion.fileUploads.send
の戻り値をawait Promise.all(uploadPromises);
で待ち受けないとエラーがでます - 最後に
notion.fileUploads.complete
で完了を待たないとブロック追加時にpendingなので処理できないという旨のエラーがでます - 戻り値のidを使って
notion.blocks.children.append
します
使い方
@notionhq/clientの最新版をインストールします。 (使用したのは4.0.1でした)
上記のコード(png_upload_test.js)と、small.pngとbig.pngを同じフォルダに置きます。
コードのpageIdの部分はアクセス可能なページIdをいれてください。
ターミナルで以下を実行します。
node png_upload_test.js (notionトークン)
まとめ
single_partの方は参考にさせていただいた記事からすんなりできたのですが、multi_partの方もnotion apiを使って画像アップロードしようとしたのでいろいろと苦労しました。
同じような対応をしようとしている方の参考になれば幸いです。
Discussion