SharePoint ドキュメント ライブラリに Microsoft Graph API 経由でファイルをアップロードしつつ、必須列のメタデータや保持ラベルの制約も満たしたい――そんなときの実務的な実装パターンを詳しく解説します。C# / Graph SDK と生 HTTP の両方で、安定して動く「二段階アップロード方式」を中心に整理します。
Graph APIで「メタデータ付きアップロード」が悩ましい理由
Microsoft Graph の /drive/root:/path/to/file:/content などのエンドポイントを使うと、SharePoint ドキュメント ライブラリにファイル本体をアップロードできます。しかし、次のような要件を同時に満たそうとすると、途端に難易度が上がります。
- ライブラリに設定された 必須列(Required columns) をアップロードと同時に埋めたい
- アイテムに 保持ラベル(Recordラベル) が付き、メタデータ編集が制限されている
- Power Automate の 「新規ファイルが作成されたとき」トリガー が、メタデータ未設定のタイミングで走ってしまいフローが失敗する
ドキュメントを探しても「ファイル本体とメタデータを完全に同時に一発登録」する汎用的な Graph API は見つかりません。そのため、実務では次のような 二段階パターン を採用するのが現実的なベストプラクティスになります。
結論:Graph APIでは「本体アップロード → fields PATCH」の二段階が実務ベスト
実務で安定させるなら、処理の流れを次の二段階に分けるのがおすすめです。
- Graph APIで ファイル本体をアップロード する(小容量:単発 PUT、大容量:アップロード セッション)。
- アップロード直後に、対応する ListItem の
fieldsに PATCH して必須列を埋める。必要に応じて チェックイン を実行する。
| ステップ | 目的 | 主なAPI |
|---|---|---|
| 1 | ファイル本体のアップロード | PUT /drive/root:/path/file:/contentcreateUploadSession(大容量) |
| 2 | メタデータ(必須列)の設定 | PATCH /drive/items/{id}/listItem/fields |
| 3(任意) | チェックイン・公開 | POST /drive/items/{id}/checkin |
このパターンのポイントは、「アップロードが完了し、ListItem が生成されてから」 メタデータを埋めることです。こうすることで、必須列やチェックイン設定・保持ラベルなどのサイト設定に合わせて、安全に業務フローへ乗せられます。
前提知識:DriveItemとListItemの関係を理解する
Graph で SharePoint のファイルを扱うとき、よく出てくるのが DriveItem と ListItem です。
| オブジェクト | 役割 | よく使うプロパティ |
|---|---|---|
| DriveItem | ファイル/フォルダそのもの。コンテンツやバージョン、チェックイン状態など。 | id, name, webUrl, file, folder, listItem |
| ListItem | ドキュメント ライブラリの行(アイテム)。列値=メタデータを保持。 | id, fields, contentType |
ドキュメント ライブラリでは、1つのファイル=1つの DriveItem + 1つの ListItem のペアとして存在しています。メタデータ(タイトル、案件番号、区分など)は ListItem の fields に保存されるため、Graph でメタデータを触るときは listItem/fields への PATCH が本命になります。
小容量ファイルの実装例(C# / Graph SDK)
まずは、数MB程度までの小さなファイルを想定した C# / Graph SDK の最小実装例です。
// using Microsoft.Graph;
// using Microsoft.Graph.Models;
// 事前に GraphServiceClient graphClient が生成済みとする
// driveId は対象ライブラリのドキュメント ライブラリに対応する Drive の ID
var filePath = @"C:\temp\estimate.pdf";
var folderPath = "見積/2025";
var fileName = "案件A_見積書.pdf";
using var fileStream = File.OpenRead(filePath);
// 1) 本体アップロード(小容量ファイル)
var uploaded = await graphClient
.Drives[driveId]
.Root
.ItemWithPath($"{folderPath}/{fileName}")
.Content
.PutAsync<DriveItem>(fileStream);
// アップロードに成功すると DriveItem が返ってくる
var driveItemId = uploaded.Id;
// 2) メタデータ更新(必須列を埋める)
var fieldValueSet = new FieldValueSet
{
AdditionalData = new Dictionary<string, object>
{
// Title などの列は「内部名」で指定すること
{ "Title", "案件A_見積書" },
{ "ProjectNo", "PJ-2025-001" },
{ "Customer", "Contoso株式会社" },
{ "Amount", 1200000 } // 数値列の場合は数値で
}
};
await graphClient
.Drives[driveId]
.Items[driveItemId]
.ListItem
.Fields
.PatchAsync(fieldValueSet);
// 3) 必要ならチェックイン
await graphClient
.Drives[driveId]
.Items[driveItemId]
.Checkin
.PostAsync(new CheckinPostRequestBody
{
Comment = "初回登録",
CheckInAs = "0" // 省略可/Major/Minor の扱いはライブラリ設定に依存
});
ポイントを表で整理します。
| ポイント | 内容 |
|---|---|
| Drive ID | SharePoint サイトのドキュメント ライブラリごとに固有の driveId がある。事前に /sites/{siteId}/drives などで調べておく。 |
| フォルダ パス | ItemWithPath でフォルダ階層を指定。フォルダがなければエラーになるので、必要に応じて事前に作成。 |
| 内部名 | 列は必ず「表示名」ではなく「内部名」で指定する。 SharePoint の列設定画面や /lists/{id}/columns の Graph から確認可能。 |
| チェックイン | バージョン管理・チェックインが有効なライブラリでは、必須列を埋めたあとにチェックインすることで、他ユーザーに見える「正式版」として登録される。 |
HTTP(curl)でのGraph API呼び出しイメージ
C# 以外の言語やシェルスクリプトで簡単に試したい場合は、curl で HTTP 直接叩いてみるのが手っ取り早いです。以下は雰囲気をつかむための例です。
1. 本体アップロード
# access_token は事前に取得済みとする
ACCESS_TOKEN="{access_token}"
FILE_PATH="./estimate.pdf"
SITE_DRIVE_ID="{drive-id}"
FOLDER_PATH="見積/2025"
FILE_NAME="案件A_見積書.pdf"
curl -X PUT \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/pdf" \
--data-binary @"$FILE_PATH" \
"https://graph.microsoft.com/v1.0/drives/$SITE_DRIVE_ID/root:/$FOLDER_PATH/$FILE_NAME:/content"
レスポンス JSON から id を控えておきます(これが driveItem-id)。
2. メタデータ更新(fields PATCH)
DRIVE_ITEM_ID="{driveItem-id}"
curl -X PATCH \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Title": "案件A_見積書",
"ProjectNo": "PJ-2025-001",
"Customer": "Contoso株式会社",
"Amount": 1200000
}' \
"https://graph.microsoft.com/v1.0/drives/$SITE_DRIVE_ID/items/$DRIVE_ITEM_ID/listItem/fields"
3. チェックイン
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"comment": "初回登録",
"checkInAs": "major"
}' \
"https://graph.microsoft.com/v1.0/drives/$SITE_DRIVE_ID/items/$DRIVE_ITEM_ID/checkin"
大容量ファイルの場合:Upload Sessionを使う
数十MB〜数GBといった大きなファイルは、単発 PUT ではなく Upload Session を使って分割アップロードします。流れ自体は小容量と同じで、「アップロード完了 → fields PATCH → 必要ならチェックイン」の三段階です。
大容量アップロードの典型的な流れ
POST /drive/root:/path/file:/createUploadSessionでアップロード セッションを作成。- 返却された
uploadUrlに対して、PUTでチャンク(分割したバイト列)をアップロード。 - すべてのチャンクを送信し終えると、最終レスポンスとして DriveItem が返る。
- その DriveItem の
idを使って/listItem/fieldsに PATCH。
// 1) Upload Session 作成
var uploadSession = await graphClient
.Drives[driveId]
.Root
.ItemWithPath($"{folderPath}/{fileName}")
.CreateUploadSession
.PostAsync(new CreateUploadSessionPostRequestBody());
// 2) 分割アップロード
int maxChunkSize = 320 * 1024; // 320 KB など
var provider = new ChunkedUploadProvider(
uploadSession,
graphClient,
fileStream,
maxChunkSize);
var uploadTasks = provider.GetUploadChunkRequests()
.Select(request => provider.GetChunkRequestResponseAsync(request, CancellationToken.None));
await Task.WhenAll(uploadTasks);
// 3) 完了後の DriveItem を取得
var completedItem = await provider.CommitAsync();
var driveItemId = completedItem.Id;
// 4) fields PATCH は小容量と同じ
Upload Session を使っても、「最後に DriveItem が1つ出来上がり、それに ListItem が紐づく」という構造は変わりません。したがって、メタデータの扱いは小容量と同じ考え方で OK です。
必須列とチェックイン/チェックアウトの設計ポイント
ドキュメント ライブラリに必須列があると、ファイルアップロード直後は次のような状態になることがあります。
- アイテムは「下書き」扱い
- アップロードしたユーザーにのみ見える(他のユーザーからは非表示)
- 「必須列が埋まるまでチェックインできない」などの制約
Graph で扱う場合、次の順番にするのが安全です。
- 本体アップロード
listItem/fieldsへの PATCH で必須列をすべて埋める- (バージョン管理が有効なら)チェックインする
| 設定 | Graph 側で意識すること |
|---|---|
| 必須列 | アップロード直後に必ず PATCH する。抜け漏れがあるとチェックインできない。 |
| バージョン管理 | メジャー/マイナー バージョンの扱いを把握し、必要なら Checkin API を呼ぶ。 |
| 承認ワークフロー | 承認フローが走るタイミングを意識し、メタデータが揃ってから開始されるように設計。 |
Power Automateがメタデータ未設定で走って失敗する問題への対処
Power Automate の 「ファイルが作成されたとき」トリガー は、Graph によるアップロード直後の「メタデータ未設定状態」でも発火します。その結果、フロー内で必須列を参照したときに null になり、エラーになることがよくあります。
これを避ける主なパターンは、次の3つです。
トリガー条件で「必須列が空」のときは起動しない
もっともシンプルなアプローチは、Power Automate 側のトリガーに 条件式 を設定して、必須列が空のときは起動させないことです。
例:Title と ProjectNo の両方が埋まっているときだけフローを開始する条件
@and(
not(empty(triggerOutputs()?['body/Title'])),
not(empty(triggerOutputs()?['body/ProjectNo']))
)
| メリット | デメリット |
|---|---|
| 実装が簡単。既存フローの修正も比較的容易。 | Graph 以外(ユーザーの手動アップロード等)でも同じ条件が適用されるため、要件によっては注意が必要。 |
ステージング用ライブラリ/フォルダを用意してから移動する
もう一歩踏み込んだパターンとして、次のような「ステージング」運用があります。
- Graph は「ステージング用ライブラリ(またはフォルダ)」にファイルをアップロード。
- Graph でメタデータをすべて埋める。
- Graph の
moveAPI などで、最終格納先ライブラリ/フォルダへ移動する。 - Power Automate のトリガーは「最終格納先」だけを監視する。
こうすると、最終格納先に現れるときには既にメタデータが揃っているため、フロー内では必須列が前提として利用できます。
「作成または変更時」トリガー+トリガー条件の組み合わせ
「作成時」トリガーではなく「ファイルが作成または変更されたとき」トリガーを使い、次のように設計することもできます。
- 作成直後:メタデータが空なので、トリガー条件でスキップ。
- Graph からの
fieldsPATCH 後:必須列が埋まるため、条件を満たしてフローが起動。
このパターンは、ユーザーによる手動更新など、Graph 以外の変更も含めて「必須列が揃ったタイミング」でフローを走らせたい場合に有効です。
コンプライアンス(保持ラベル等)でメタデータ編集が制限される場合
SharePoint の保持ラベルやレコード管理設定によっては、次のような制約がかかることがあります。
- ラベルが Record モードになっており、コンテンツやメタデータの編集が禁止されている。
- 特定の列だけ編集不可/編集可能、などの細かな設定。
このようなアイテムに対して Graph から listItem/fields に PATCH すると、403 Forbidden や 409 Conflict が返る場合があります。その場合、アプリの実装だけでは解決できず、テナント側のガバナンス設計とセットで見直す必要があります。
| 対処パターン | 概要 | 注意点 |
|---|---|---|
| ラベル構成を見直す | レコード ラベルの設定を「メタデータは編集可能」に変更する。 | コンプライアンス/監査要件とのバランスを考慮すること。 |
| 記録管理者による一時的なアンロック | 管理者が一時的にロックを解除して更新し、その操作を監査ログに残す。 | 運用コストが高くなるため、定常的な運用には向きにくい。 |
| ラベル適用前にメタデータを設定 | アップロード → メタデータ設定 → ラベル自動付与、の順になるようにルールやフローを設計する。 | 自動ラベル付与ポリシーとの連携を確認する必要がある。 |
いずれの場合も、Graph アプリの権限だけでは越えられない境界であることを理解し、セキュリティ担当者や情報管理担当者と協議した上で設計することが重要です。
SharePoint REST APIを併用する高度なパターン
「Graph に統一したい」という方針は合理的ですが、一部の高度なシナリオでは SharePoint REST API を併用した方が設計しやすいケースもあります。
特に、次のような要件がある場合です。
- フィールド更新時にサーバー側の検証ロジックを厳密に走らせたい
- コンテンツタイプや複雑な列セットを、1回の API 呼び出しで検証付き登録したい
SharePoint REST には ValidateUpdateListItem や AddValidateUpdateItemUsingPath など、フィールド更新+検証 を一気に実行できるエンドポイントが存在します。たとえば、次のようなパターンです。
- ファイル本体のアップロードは Graph で行う。
- その後、アイテムのフィールド更新だけ SharePoint REST の検証付き API を使う。
このように「ファイル関連は Graph、フィールドの検証付き更新は REST」と役割を分けることで、既存の SharePoint ベースのワークフローと整合性の高い実装にできる場合があります。ただし、アプリ側の実装や保守性は少し複雑になるため、プロジェクトの規模や要件に応じて選択するとよいでしょう。
実装パターン比較:どれを選ぶべきか
| パターン | 概要 | 向いているケース |
|---|---|---|
| Graph 二段階(本体→fields PATCH) | もっともシンプルな標準パターン。Graph SDK/HTTP だけで完結。 | ほとんどの業務シナリオ。カスタム列が多い場合でも対応しやすい。 |
| Graph + Power Automate トリガー条件 | Graph 側でメタデータを設定しつつ、フロー側のトリガー条件で「未設定」を弾く。 | 既に多くのフローが動いていて、Graph 実装を最小限に抑えたい場合。 |
| ステージング ライブラリ+移動 | 一旦ステージングにアップロードし、メタデータ設定後に最終ライブラリに移動。 | 最終ライブラリでは人手によるアップロードも混在し、フロー側ロジックを単純に保ちたい場合。 |
| Graph + SharePoint REST | ファイルは Graph、フィールド更新は REST の検証付き API。 | 既存の REST ベース開発資産があり、厳密な検証ロジックを使い回したい場合。 |
よくあるエラーとトラブルシューティング
実装時によく遭遇するエラーを、原因と対処とあわせて整理します。
| 現象・エラーメッセージ | 主な原因 | 対処ポイント |
|---|---|---|
The property 'ProjectNo' does not exist on type 'SP.Data...' | 列の「内部名」が誤っている。表示名をそのまま使っている。 | サイト設定画面や Graph の /lists/{id}/columns で内部名を確認し、正しいキー名で PATCH する。 |
403 Forbidden / Access Denied | アプリに必要な Microsoft Graph の権限(Files.ReadWrite.All, Sites.ReadWrite.All 等)が付与されていない、もしくは保持ラベルの制限。 | アプリ登録の API 許可を見直し、管理者承認を得る。保持ラベルの設定を確認し、メタデータ編集が許可されているかを検証。 |
409 Conflict(チェックイン関連) | 必須列が埋まっていないのにチェックインしようとしている。 | チェックイン前に必須列をすべて PATCH しているか確認する。必要なら、PATCH のレスポンスを検証してからチェックインする。 |
| Power Automate フローが null 参照で失敗 | メタデータ未設定の状態でトリガーが走り、式中の参照が null。 | トリガー条件を追加して必須列が空のときは起動しないようにするか、「作成または変更時」トリガー+条件でメタデータが揃ったタイミングだけ処理する。 |
| ファイルは見えるが、別ユーザーには見えない | チェックインされていない下書き状態。 | Graph の Checkin API を呼ぶか、ライブラリのバージョン管理設定を見直す。 |
列の型とPATCH時の値の例
列の型と、Graph で PATCH するときの JSON 値の例を簡単に整理しておきます。実際にはサイトの設定やコンテンツタイプによって異なるため、開発前にテスト環境で確認するのがおすすめです。
| SharePoint 列の種類 | 値の例 | 備考 |
|---|---|---|
| 単一行テキスト | { "Title": "案件A_見積書" } | 文字列として送るだけ。 |
| 数値 | { "Amount": 1200000 } | 数値型で送る。文字列にすると型変換エラーになる場合がある。 |
| はい/いいえ | { "IsApproved": true } | 真偽値(true/false)。 |
| 日付と時刻 | { "DueDate": "2025-12-31T00:00:00Z" } | ISO 8601 形式。タイムゾーンは要件に合わせて指定。 |
| 選択肢(単一) | { "Status": "承認待ち" } | 選択肢に存在する値を文字列で指定。 |
| ユーザー(単一) | { "Owner": "[email protected]" } 等 | サイト列の種類や構成により表現が変わるため、実際に UI から設定したサンプルを GET して値の構造を確認するのが確実。 |
設計チェックリスト
最後に、Graph API で「メタデータ付きアップロード」を実装するときに確認しておきたい項目をチェックリスト形式でまとめます。実装前のレビューやテスト時の見落とし防止に使えます。
- 列の 内部名 を正しく把握しているか(表示名と混同していないか)。
- 必須列の一覧を洗い出し、アップロード直後に漏れなく PATCH しているか。
- ドキュメント ライブラリの バージョン管理・チェックイン設定 を確認し、必要なら Checkin API を組み込んでいるか。
- Power Automate のトリガー条件で、「必須列が空」の状態でフローが起動しないようにしているか。
- 保持ラベルやレコード管理設定により、Graph 経由のメタデータ編集が制限されていないか。
- 大容量ファイルに対しては Upload Session を使い、アップロード完了後に fields PATCH を行うようにしているか。
- エラー時のログ出力や再試行戦略(リトライ、デッドレターキューなど)を設計しているか。
- ステージング ライブラリ/フォルダや SharePoint REST 併用など、将来の拡張余地を考慮しているか。
まとめ:完全同時登録にこだわらず、二段階で堅実に
Microsoft Graph API は日々進化していますが、少なくとも現時点では「ファイル本体とメタデータを完全に同時に一発登録」するための汎用 API は想定されていません。そのため、実務では次のような考え方が堅実です。
- 本体アップロード → ListItem fields PATCH → 必要ならチェックイン の二段階(三段階)パターンを基本とする。
- Power Automate のトリガー条件やステージング運用で、メタデータ未設定の状態では業務フローを走らせない ようにする。
- 保持ラベルやレコード管理で編集不可なアイテムについては、アプリ側ではなく ガバナンス設計側で解決すべき問題 として整理する。
- どうしても「作成と検証付更新」を一気にやりたい場合は、SharePoint REST API の併用 も選択肢として検討する。
「完全同時一発登録」を目指して無理に複雑な実装にするよりも、Graph の得意な部分に絞ってシンプルに設計し、メタデータが揃った段階でフローや承認プロセスを開始する――そんな設計の方が、運用面でもトラブルを減らしやすくなります。この記事をベースに、自社の SharePoint / Microsoft 365 ガバナンスに合った実装パターンを検討してみてください。

コメント