Zettelkasten Inbox / ブロック詳細
保存ボタンより先に、画像だけをNotionへ預けてしまうブロック。本当の成果物は画像ではなく、中身から計算した64桁の指紋で、これが8番の二重貼り防止の目印になる。
抽象度 Lv.4(技術用語+システム上のやりとりまで/製品名・ライブラリ名は書かない)コメントを書きながら、スクリーンショットや図を一緒に残すための仕組み。 貼り付け・ドラッグ&ドロップ・ファイル選択の3つの入口があり、 保存ボタンを押す前に、画像だけ先にNotionへ預けてしまう。
ここで作られるいちばん大事なものは、画像そのものではなく 画像の中身から計算した64桁の「指紋」である。 この指紋が、8番で「同じ画像を二重に貼らない」ための目印になる。
6. ABC Note候補の絞り込み(と並行) → 7. 画像の添付 → 8. 保存:Notionへの書き込み一式
POST /api/uploads。詳しくは5章の各工程に書いたsingle_part 方式)、続けて実体を送るuploaded でなければ 502保存より前に預けてしまう理由。保存(8番)は 「ABC Noteの本文を読む → 転記する → 画像を貼る → プロパティを書き換える → 照合する」という長い処理。 ここに10MBの画像の送信が混ざると、1回の要求が非常に長くなり、 途中で切れたときに何が書けて何が書けていないか分からなくなる。 画像を先に預けておけば、8番が送るのは短い預かり番号と指紋だけで済む。
指紋をここで作る理由。指紋は画像の中身から決まる値なので、 同じ画像なら何度アップロードしても同じ値になる。 8番はこの値を本文に埋め込んでおき、次に来たとき「もう入っているか」を文字列として探せる。 Notionの本文には「この画像はどのノートの何番目」という情報を持つ場所が無いので、 指紋を目印として本文の中に隠しておく、という発想。
ブラウザ側とサーバー側で同じ検査を二度やる理由。 ブラウザ側の検査は親切のため(選んだ瞬間に理由が出る)。 サーバー側の検査は安全のため(画面を通さず直接APIを叩かれても効く)。 役割が違うので、重複ではなく必要な二重化。
| 工程 | 主体 | やりとりの内容 | 方向 |
|---|---|---|---|
| 7-1 | ブラウザ(内部) | 形式・容量・枚数の検査と、理由の表示 | ↻ |
| 7-2 | ブラウザ → 添付API | 画像1枚(ファイルとして) | → |
| 7-3 | 添付API(内部) | 形式・容量の再検査 | ↻ |
| 7-4 | 添付API(内部) | 中身から64桁の指紋を計算 | ↻ |
| 7-5 | 添付API → Notion | 預かり枠の作成(名前・種類・方式) | → |
| 7-6 | 添付API → Notion | 画像の実体の送信 | → |
| 7-7 | 添付API(内部) | 預かり状態の確認 | ↻ |
| 7-8 | 添付API → ブラウザ | 預かり番号・指紋・名前・種類・大きさ | ← |
7-1 画像は3つの入口から入る。コメント欄への貼り付け、 枠へのドラッグ&ドロップ、ボタンからのファイル選択。 受け取った瞬間に、形式・大きさ・枚数を確かめて、駄目なものは理由を出して弾く。
技術的には 貼り付けの経路では、
クリップボードの中身のうち種類が「ファイル」で、かつ形式が image/ で始まるものだけを拾う。
このとき同じクリップボードに文字が入っていなければ、貼り付けの既定の動作を打ち消す――
打ち消さないと、画像と一緒にファイル名などの文字がコメント欄に紛れ込むため。
文字が入っている場合は打ち消さないので、文字はそのまま貼られ、画像は添付に回る。
検査は3つ:形式が4種類(image/png/image/jpeg/image/webp/image/gif)のどれか、
大きさが0より上で10MB以下か、残り枠(5枚まで)に収まるか。
弾いたものは「◯◯.png:対応していない画像形式です。」のようにファイル名付きで理由を出す。
表示用の縮小画像は、ブラウザの中だけで有効な一時的なURLを作って使い、
削除時と画面を離れるときに必ず解放する(放っておくとメモリを食い続けるため)。
7-2 受理した画像は、1枚ずつ別々にサーバーへ送る。 複数枚あっても順番待ちはせず、同時に走る。
技術的には POST /api/uploads に
multipart/form-data で1枚。
これはファイルと文字を1つの送信にまとめる形式で、区切りの文字列で各部分を分ける。
画像のような生のデータは application/json にそのままは入れられないため、
文字に変換して膨らませる(base64は約1.33倍になる)か、この形式を使うかの二択で、後者を選んでいる。
枚数ぶんの要求が同時に飛ぶので、5枚なら5本並行。
画面側は1枚ごとに「アップロード中/添付済み/失敗」の状態を持ち、
1枚でも「アップロード中」か「失敗」があると保存ボタンが押せない。
7-3 サーバーは、送られてきたものが本当に画像かをもう一度確かめる。
技術的には 送信の中身を解析して file の部分を取り出し、
それが本当にファイルとして届いているか、形式が4種類のどれか、
大きさが0より上で10MB以下かを見る。外れたら 400 と日本語の理由。
ここで見ているのは送信側が申告した形式であって、中身のバイト列は検査していない。
拡張子と申告を偽った別種のファイルは通りうるが、
最終的にNotion側でも形式が確認されるので、二重の網にはなっている。
7-4 画像の中身全部を材料に、64桁の文字列を計算する。 同じ画像なら必ず同じ値になり、1バイトでも違えば全く別の値になる。
技術的には SHA-256という ハッシュ関数(どんな長さのデータからも決まった長さの値を作る計算。 元に戻せず、少し違うだけで結果が大きく変わる)を、ファイル全体のバイト列にかける。 結果の32バイトを16進数に直すと64文字になる。 この値がこのブロックの本当の成果物。 8番はこれを本文に埋め込み、次に同じ画像が来たとき 「その文字列が本文のどこかにあるか」を探すだけで二重貼りを防げる。 ファイル名や送信の順番ではなく中身そのものから決まるので、 同じ画像を名前を変えて貼り直しても同じ指紋になる。
7-5 Notionへ「これから画像を1つ預けます」と伝えて、預かり枠を作ってもらう。
技術的には POST /v1/file_uploads に
application/json で、方式(single_part=分割せず一度に送る)・
ファイル名・形式を送る。返ってくるのは預かり番号。
Notionのファイルアップロードは、枠を作る要求と実体を送る要求の2段階に
分かれているので、ここも2回に分かれる。
7-6 作ってもらった枠に、画像の実体を送り込む。
技術的には POST /v1/file_uploads/{番号}/send に
multipart/form-data で。
この1回だけは Content-Type をこちらから指定しない――
区切りの文字列は送信の仕組みが自動で決めるので、手で書くとずれる。
ここでも 429 に対する自動再送(最大2回、1〜5秒待ち)が効く。
7-7 Notionが「預かりました」と言っているかを確かめる。 言っていなければ失敗として扱う。
技術的には 返ってきた状態が uploaded かどうかだけを見る。
違えば 502 +「画像をNotionへアップロードできませんでした。もう一度お試しください。」。
他のブロックと同じ「書いたら読み直して確かめる」方針がここにも出ている。
7-8 預かり番号と指紋を画面へ返す。画像そのものは、もうブラウザからサーバーへ送らない。
技術的には {"uploadId": "…", "digest": "64桁", "filename": …, "contentType": …, "size": …}。
画面はこのうち番号と指紋の2つだけを保存用にため込み、8番の保存時にまとめて送る。
つまり保存の要求に画像のデータは1バイトも乗らない。
8番の側では、この番号を使ってもう一度Notionに預かり状態を問い合わせ直す
(画面から送られてきた番号を鵜呑みにしないため)。
400。
その1枚だけが「失敗」になり、他の画像は影響を受けない。502。画面のその1枚に
「再試行」ボタンが出る。押すと同じファイルで最初からやり直す。※ 2026年8月14〜15日時点の zettelkasten-inbox のプログラムを読んで作成しました。過去の設計資料は参照していません。
※ 番号は全体図・この解説・シーケンス図で共通です。工程番号は「ブロック番号-連番」で書いています。