NotionRubyMapping のアップデート(17) : hkob の雑記録 (175)

はじめに

hkob の雑記録の第175回目は、10MB 以上のファイルを分割して送付する仕組みのテストファイルを作成します。

File の分割

20MB 以下のファイルは先日の単一ファイルのアップロードで済みますが、20MB を超える場合には分割送付が必要です。説明では 10MB くらいで分割するとよいと説明されています。テストデータとしては約12MB (11,916,526 bytes) の sample-15s.mp4 ファイルを用意しました。10MB 単位でファイルに分割する Ruby コードを書いて実際にファイルを分割してみました。

file = "spec/fixtures/sample-15s.mp4"
tempfiles = []
File.open(file, "rb") do |file|
  until file.eof?
    chunk = file.read 10*1024*1024
    t = Tempfile.new "split"
    t.write chunk
    tempfiles << t
    p t.path
  end
end

ここで作成された Tempfile のパスは以下のようになりました。

"/var/folders/cw/9fjhttb17jbb3233xc3k9l3c0000gp/T/split20250622-98904-sy5cs0"
"/var/folders/cw/9fjhttb17jbb3233xc3k9l3c0000gp/T/split20250622-98904-xpxrhe"

File upload object の作成

先日の file upload object と同様に今回もまず、file upload object を作成する create_file_upload_video.sh を作成します。前回のものと異なるのは、data の部分に multi_part であること、ファイルの数が 2 つであること、最終的なファイル名が sample-15s.mp4 になることを知らせている点です。

#!/bin/sh
curl --request POST \
  --url 'https://api.notion.com/v1/file_uploads' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H 'Content-Type: application/json' \
  -H 'Notion-Version: 2022-06-28' \
  --data '{
    "mode": "multi_part",
    "number_of_parts": 2,
    "filename": "sample-15s.mp4"
  }'

実行して make で json を取得しておきます。

touch *.json; make
sh create_file_upload_video.sh > create_file_upload_video.json
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   665  100   575  100    90   1352    211 --:--:-- --:--:-- --:--:--  1561
sleep 1

取得した JSON は以下のようになりました。ここで示された upload_url に対してファイルを送信します。

{
    "object": "file_upload",
    "id": "21ad8e4e-98ab-814e-8d96-00b2ded97d6c",
    "created_time": "2025-06-22T07:16:00.000Z",
    "created_by": {
        "id": "40673a87-d8ed-41e0-aa55-7f0e8ace24cd",
        "type": "bot"
    },
    "last_edited_time": "2025-06-22T07:16:00.000Z",
    "expiry_time": "2025-06-22T08:16:00.000Z",
    "upload_url": "https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/send",
    "archived": false,
    "status": "pending",
    "filename": "sample-15s.mp4",
    "content_type": "application/mp4",
    "content_length": null,
    "number_of_parts": {
        "total": 2,
        "sent": 0
    },
    "request_id": "d4872ae8-67e0-4bf5-8633-fe316bdc808b"
}

ファイルのアップロード

1つ目のファイルアップロードスクリプトは以下のように作成しました。単一ファイルと異なるのは part_number がある点です。

curl --request POST \
  --url 'https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/send' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H 'Notion-Version: 2022-06-28' \
  -H 'Content-Type: multipart/form-data' \
  -F "file=@/var/folders/cw/9fjhttb17jbb3233xc3k9l3c0000gp/T/split20250622-98904-sy5cs0" \
  -F "part_number=1"

レスポンスは以下のようになりました。number_of_parts の sent が 1 になっています。

{
    "object": "file_upload",
    "id": "21ad8e4e-98ab-814e-8d96-00b2ded97d6c",
    "created_time": "2025-06-22T07:16:00.000Z",
    "created_by": {
        "id": "40673a87-d8ed-41e0-aa55-7f0e8ace24cd",
        "type": "bot"
    },
    "last_edited_time": "2025-06-22T07:16:00.000Z",
    "expiry_time": "2025-06-22T08:16:00.000Z",
    "upload_url": "https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/send",
    "complete_url": "https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/complete",
    "archived": false,
    "status": "pending",
    "filename": "sample-15s.mp4",
    "content_type": "application/mp4",
    "content_length": 10485760,
    "number_of_parts": {
        "total": 2,
        "sent": 1
    },
    "request_id": "b23b3c35-a5e4-480c-b2a4-7044753de074"
}

二つ目のスクリプトも同様に準備します。

curl --request POST \
  --url 'https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/send' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H 'Notion-Version: 2022-06-28' \
  -H 'Content-Type: multipart/form-data' \
  -F "file=@/var/folders/cw/9fjhttb17jbb3233xc3k9l3c0000gp/T/split20250622-98904-xpxrhe" \
  -F "part_number=2"

レスポンスは以下のようになりました。

{
    "object": "file_upload",
    "id": "21ad8e4e-98ab-814e-8d96-00b2ded97d6c",
    "created_time": "2025-06-22T07:16:00.000Z",
    "created_by": {
        "id": "40673a87-d8ed-41e0-aa55-7f0e8ace24cd",
        "type": "bot"
    },
    "last_edited_time": "2025-06-22T07:16:00.000Z",
    "expiry_time": "2025-06-22T08:16:00.000Z",
    "upload_url": "https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/send",
    "complete_url": "https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/complete",
    "archived": false,
    "status": "pending",
    "filename": "sample-15s.mp4",
    "content_type": "application/mp4",
    "content_length": 11916526,
    "number_of_parts": {
        "total": 2,
        "sent": 2
    },
    "request_id": "1dce80d6-35c1-4937-bb99-eabe0986c24b"
}

複数ファイルのアップロードの際には、完了を指示する必要があります。complete a file upload API を呼びます。

curl --request POST \
    --url https://api.notion.com/v1/file_uploads/21ad8e4e-98ab-814e-8d96-00b2ded97d6c/complete \
    -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
    -H 'Content-Type: application/json' \
    -H 'Notion-Version: 2022-06-28'

レスポンスは以下のようになりました。複数ファイルの場合には、これで全てアップロードが完了します。

{
    "object": "file_upload",
    "id": "21ad8e4e-98ab-814e-8d96-00b2ded97d6c",
    "created_time": "2025-06-22T07:16:00.000Z",
    "created_by": {
        "id": "40673a87-d8ed-41e0-aa55-7f0e8ace24cd",
        "type": "bot"
    },
    "last_edited_time": "2025-06-22T07:28:00.000Z",
    "expiry_time": "2025-06-22T08:16:00.000Z",
    "archived": false,
    "status": "uploaded",
    "filename": "sample-15s.mp4",
    "content_type": "application/mp4",
    "content_length": 11916526,
    "number_of_parts": {
        "total": 2,
        "sent": 2
    },
    "request_id": "0295d4b2-c58f-4e3d-bc54-b8c1ee05d2e4"
}

File upload object が生成できても、ブロックなどに付与しないと1時間で消えてしまいます。テストページには video block を用意しているので、ここに貼り付けましょう。

#!/bin/sh
curl -X PATCH 'https://api.notion.com/v1/blocks/20bd8e4e98ab80269cd7e7c36a2072a0' \
  -H 'Notion-Version: 2022-06-28' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H 'Content-Type: application/json' \
  --data '{
      "video": {
        "file_upload":{
          "id": "21ad8e4e98ab814e8d9600b2ded97d6c"
        }
      }
  }'

レスポンスは以下のようになりました。

{
    "object": "block",
    "id": "20bd8e4e-98ab-8026-9cd7-e7c36a2072a0",
    "parent": {
        "type": "page_id",
        "page_id": "20bd8e4e-98ab-80c7-9576-dcf6f6e5ee4a"
    },
    "created_time": "2025-06-07T10:00:00.000Z",
    "last_edited_time": "2025-06-22T07:45:00.000Z",
    "created_by": {
        "object": "user",
        "id": "2200a911-6a96-44bb-bd38-6bfb1e01b9f6"
    },
    "last_edited_by": {
        "object": "user",
        "id": "40673a87-d8ed-41e0-aa55-7f0e8ace24cd"
    },
    "has_children": false,
    "archived": false,
    "in_trash": false,
    "type": "video",
    "video": {
        "caption": [],
        "type": "file",
        "file": {
            "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/2b7b01f0-67a8-40f8-acd4-88dd2805f216/eb854567-eb3e-4877-b327-050a4c8541f4/sample-15s.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB4664MHGDGQA%2F20250622%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20250622T074534Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEPz%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLXdlc3QtMiJIMEYCIQCMj%2BjaDo5dhfdW%2B%2BjocHdrPqBoOC8ohbQtfbNmA0HUZgIhAOfbxWI9tAYoRHGOcqu5OBFcW4sdOAmUUBbXWs7J68xPKogECOX%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMNjM3NDIzMTgzODA1Igy3i2jSwRMOM6N%2Fmqwq3AOxR%2BpsNBwwuSb5e3fWitb21Ytk7I2stTCGbbGe46c9wVu6AvJweYf3WvATZwjYHd07Y52HvbKYGGrce6Iw0nCnXu2i71b6bBaAZVUXQT8vpJrKiUfAR2wDHCtXZ%2BujxMvkWQSwpSZ7XVrGxxVpfJJukwDj6ZZlHFtYznYPlsOx7Wovz8YuOO6G2Ojwp5i%2FfNcrqBLm0e84%2BZbUg7XJLJAMI%2FkdHJGOkmFOZjworNwT0tKjF8UDn0ccv%2BtRbM6930qNyfD4t6BM9VE9SKHx35dfm95H%2FOPcQmO1HPUE1lW2ESIv6To3G0YpTtr%2FOwRKWzfsoKevjsw%2FefAzutBTJfJDyAHE6o3cbmf5FzZcLVEIxvGH0Gu2PFhIP2Ti28NlpDJJHpnJhWG2HVnJPDH2K4eRkRWfnKmNA07%2F9221jw5%2BHYg6MJAaQynVuBtwLrqrub9l7m7A%2BEaPIMWChaMEpsl4tE2CO4BtJ7f4P2HvZlpqJPmxSLPWV7o%2FKgThysqgJ2%2Fs2iuhi1RbMOuXPiAZJYzuDSRi9b%2FGPv6thDA%2FCwA61ia%2BStjBjinx5g70SCPblERlUgZIzbNl9I4oxsM1fiukMz9NrXS4WfgXWC8ppomhqAgpORUJbcEwWzMFiTCR%2FN3CBjqkAU9eScFSHXdqy3Om7pMm%2ByHMRZDIORC7wl87uhUeZh0uRW98Lg4du38d%2FpzqVR4KprTckR1%2F6bvkFBJJmaKr7JeUNJBgx1MkjCaaL7RNi7ersYiVfpk%2FjfHFKhafXqyasiLn3civPkUdecoTXfL7MTgV2%2FGdbVnyYY6jag2hU6LaQk1QeF9Fu7njD%2FOYcC3mgdgCdm7nalBYkqvjByxTEMXTOKou&X-Amz-Signature=c47555fa76dc6e8228cc36dece126df1120bee30a44c366ad8573b54fe205925&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject",
            "expiry_time": "2025-06-22T08:45:34.374Z"
        }
    },
    "request_id": "3aa67d7c-605f-4c76-b5ca-9aec6dec1265"
}

おわりに

これでビデオに関するテストデータについては用意できました。明日からはこのテストデータを使って、テストおよび実装を進めていきます。

hkob.notion.site