SharePoint Online のモダン リストで使える「画像 (Image) 列」は、CSOM(C#)から直接バイナリを流し込む列ではなく、画像ファイルを指す JSON を保存します。本記事では、jpg をアップロードして Image 列に表示させる2つの実装パターン(ライブラリ保存/添付ファイル保存)を、実務で使える形に落とし込んで解説します。
SharePoint Online の「画像 (Image) 列」が扱いにくく見える理由
SharePoint Online のモダン UI で追加できる「画像 (Image) 列」は、従来の “画像を置いて URL を貼る” という感覚と少し異なります。CSOM でつまずきやすいポイントは、次の2点です。
- Image 列そのものに画像データを格納するわけではない(保存されるのは「画像を指す JSON 文字列」)
- UI 側の保存場所・JSON の形が変わることがある(特にモダン UI は内部仕様が変わりやすい)
以前は「サイト アセット」など特定のライブラリ(リスト GUID 配下)に画像が置かれ、Image 列の値も比較的読みやすい JSON であるケースが多く、開発者側で JSON を組み立てて列にセットしやすい状況がありました。しかし最近の挙動では、画像がリスト アイテムの添付ファイル フォルダーに保存されるケースが増え、UI が生成する値に「数字が混ざったファイル名」などが含まれ、意図を読み取りにくくなっています。
| 観点 | 以前よく見られた挙動 | 最近よく見られる挙動 |
|---|---|---|
| 画像ファイルの保存場所 | サイト アセット等のライブラリ(特定フォルダー) | アイテムの添付ファイル フォルダー(Attachments 配下) |
| Image 列の値 | 比較的読みやすい JSON(推測しやすい) | 追加プロパティや独特なファイル名が混ざり、意味が読みづらい |
| 実装側の安定性 | JSON を自前生成しやすい | UI 生成値の完全再現は難しいことがある |
結論として、CSOM で安定して実装するには「UI が生成する “全部入り” の値を無理に再現しようとしない」ことが重要です。Image 列は画像ファイルの場所を示す JSONを受け取る列なので、自分が管理できるパス(URL)を用意して、その参照を JSON でセットするのが最も堅実です。
Image 列の正体:保存されるのは JSON 文字列
モダンな Image 列は、内部的に「画像ファイルの URL 情報などを持つ JSON オブジェクト」を文字列として保存します。CSOM では、この JSON を文字列として列にセットします。
最低限押さえるべきプロパティ
環境や時期によって追加プロパティが含まれることはありますが、実装の芯として押さえるのは次の3つです。
| プロパティ | 意味 | 例 | 注意点 |
|---|---|---|---|
fileName | 画像ファイル名 | img.jpg | 表示や UI の参照に使われることがあります。拡張子も含めます。 |
serverUrl | テナントのルート(スキーム+ホスト) | https://tenant.sharepoint.com | /sites/... は含めず、ルートの形に揃えると安定しやすいです。 |
serverRelativeUrl | サーバー相対 URL | /sites/MySite/Shared%20Documents/images/img.jpg | スペース等は URL エンコードされている必要があります。 |
JSON の例(最小構成)
{
"fileName": "img.jpg",
"serverUrl": "https://tenant.sharepoint.com",
"serverRelativeUrl": "/sites/MySite/Shared%20Documents/images/img.jpg"
}
ここで重要なのは、Image 列に「画像本体」を流すのではなく、画像の置き場所を指す JSON を流すという点です。つまり実装の選択肢は大きく2つあります。
- 方法A:画像をドキュメント ライブラリ(共有ドキュメント等)にアップロードし、その URL を JSON にして Image 列へ
- 方法B:UI と同じように画像をアイテムの添付ファイルとしてアップロードし、その URL を JSON にして Image 列へ
方法A:画像をドキュメント ライブラリにアップロードして Image 列に設定する
まずは最も安定しやすいパターンです。画像の保管場所をライブラリ側に寄せることで、運用・権限・再利用・編集の面でメリットが出やすくなります。
この方法が向いているケース
- 同じ画像を複数アイテムで使い回したい
- 画像の版管理(バージョン管理)や差し替え運用をしっかりやりたい
- 添付ファイルを無効にしているリストでも使いたい
- UI の内部挙動に引っ張られず、実装を安定させたい
全体の流れ
- 画像を置くライブラリとフォルダーを決める(例:共有ドキュメントの
/images) - CSOM で jpg をライブラリへアップロードする
- アップロードしたファイルの
ServerRelativeUrlを取得する fileName/serverUrl/serverRelativeUrlを持つオブジェクトを JSON シリアライズ- リスト アイテムの Image 列(内部名)に JSON 文字列をセットして更新
C#(CSOM)サンプル:ライブラリへアップロード → Image 列へ反映
以下は「すでに ClientContext が認証済みで取得できている」前提のサンプルです。認証の方式(証明書認証/対話ログイン等)はテナントの要件に合わせてください。
using System;
using System.IO;
using Microsoft.SharePoint.Client;
using Newtonsoft.Json;
public class ImageValue
{
public string fileName { get; set; }
public string serverUrl { get; set; }
public string serverRelativeUrl { get; set; }
}
public class SharePointImageColumnSample
{
public static void UploadJpgToLibraryAndSetImageColumn(
ClientContext ctx,
string listTitle,
int itemId,
string imageColumnInternalName,
string libraryFolderServerRelativeUrl,
string localJpgPath)
{
if (ctx == null) throw new ArgumentNullException(nameof(ctx));
if (string.IsNullOrWhiteSpace(listTitle)) throw new ArgumentException("listTitle is required.");
if (string.IsNullOrWhiteSpace(imageColumnInternalName)) throw new ArgumentException("imageColumnInternalName is required.");
if (string.IsNullOrWhiteSpace(libraryFolderServerRelativeUrl)) throw new ArgumentException("libraryFolderServerRelativeUrl is required.");
if (!File.Exists(localJpgPath)) throw new FileNotFoundException("localJpgPath not found.", localJpgPath);
// 1) 画像をライブラリ(フォルダー)へアップロード
var fileName = Path.GetFileName(localJpgPath);
var folder = ctx.Web.GetFolderByServerRelativeUrl(libraryFolderServerRelativeUrl);
ctx.Load(folder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
Microsoft.SharePoint.Client.File uploadedFile;
using (var fs = new FileStream(localJpgPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var fileInfo = new FileCreationInformation
{
Url = fileName, // フォルダー配下のファイル名
Overwrite = true,
ContentStream = fs
};
uploadedFile = folder.Files.Add(fileInfo);
ctx.Load(uploadedFile, f => f.Name, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
}
// 2) Image 列に入れる JSON を作る
// serverUrl は「https://tenant.sharepoint.com」のようにルートに揃えるのが無難
var origin = new Uri(ctx.Url).GetLeftPart(UriPartial.Authority);
var img = new ImageValue
{
fileName = uploadedFile.Name,
serverUrl = origin,
serverRelativeUrl = uploadedFile.ServerRelativeUrl
};
string json = JsonConvert.SerializeObject(img);
// 3) リスト アイテムへセット
var list = ctx.Web.Lists.GetByTitle(listTitle);
var item = list.GetItemById(itemId);
item[imageColumnInternalName] = json;
item.Update();
ctx.ExecuteQuery();
}
}
実装のポイント(ライブラリ保存)
- 内部名を使う:列名(表示名)ではなく、Image 列の内部名で
item[internalName]を指定すると事故が減ります。 - URL エンコードに注意:
serverRelativeUrlはスペースが%20になっている必要があります(CSOM が返すServerRelativeUrlは通常この点が安全です)。 - 権限がないと壊れた画像になる:閲覧者が画像ファイルにアクセスできないと、Image 列は「画像が表示できない」状態になります。画像保管場所の権限設計を先に決めておくとスムーズです。
- ファイル名の衝突を避けたい場合:
img_<guid>.jpgのようにユニーク名にすると運用が安定します(後述の添付ファイル方式でも有効です)。
方法B:UI と同じく「アイテムの添付ファイル」にアップロードして Image 列に設定する
「モダン UI から画像を入れたときと同じ場所(添付ファイル フォルダー)に保存したい」場合は、この方法が近い挙動になります。CSOM でも通常の添付ファイルとして jpg を追加できるため、最終的にその URL を JSON に入れて Image 列にセットします。
この方法が向いているケース
- アイテム単位で画像を完結させたい(データの持ち運び・移行を意識したい)
- 「Attachments 配下」に集約される運用が都合良い
- UI の操作感に寄せたい(保存場所だけでも合わせたい)
注意:添付ファイル方式は “アイテム ID が必要”
添付ファイルは基本的に .../Attachments/<ItemId>/ に格納されます。つまり、新規アイテムの場合は先にアイテムを作って ID を確定させる必要があります。そのため「新規作成+画像添付+Image 列反映」を一発で完結させるのは難しく、処理が複数回の ExecuteQuery() になりがちです。
全体の流れ
- (新規の場合)アイテムを先に作成して
Idを確定させる - 添付ファイルとして jpg をアップロードする
- 添付ファイルの
ServerRelativeUrlを取得する - その URL を使って JSON を組み立て、Image 列にセットする
C#(CSOM)サンプル:添付ファイルへアップロード → Image 列へ反映
using System;
using System.IO;
using Microsoft.SharePoint.Client;
using Newtonsoft.Json;
public class ImageValue
{
public string fileName { get; set; }
public string serverUrl { get; set; }
public string serverRelativeUrl { get; set; }
}
public class SharePointImageAttachmentSample
{
public static void UploadJpgAsAttachmentAndSetImageColumn(
ClientContext ctx,
string listTitle,
int itemId,
string imageColumnInternalName,
string localJpgPath)
{
if (ctx == null) throw new ArgumentNullException(nameof(ctx));
if (string.IsNullOrWhiteSpace(listTitle)) throw new ArgumentException("listTitle is required.");
if (string.IsNullOrWhiteSpace(imageColumnInternalName)) throw new ArgumentException("imageColumnInternalName is required.");
if (!File.Exists(localJpgPath)) throw new FileNotFoundException("localJpgPath not found.", localJpgPath);
var list = ctx.Web.Lists.GetByTitle(listTitle);
var item = list.GetItemById(itemId);
// 添付ファイルを使うには、リスト側で添付が有効である必要があります
ctx.Load(list, l => l.EnableAttachments);
ctx.ExecuteQuery();
if (!list.EnableAttachments)
{
throw new InvalidOperationException("This list has attachments disabled (EnableAttachments = false).");
}
// 1) 添付ファイルとしてアップロード
var fileName = Path.GetFileName(localJpgPath);
Attachment addedAttachment;
using (var fs = new FileStream(localJpgPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var attInfo = new AttachmentCreationInformation
{
FileName = fileName,
ContentStream = fs
};
addedAttachment = item.AttachmentFiles.Add(attInfo);
ctx.Load(addedAttachment, a => a.FileName, a => a.ServerRelativeUrl);
ctx.ExecuteQuery();
}
// 2) Image 列の JSON を作る
var origin = new Uri(ctx.Url).GetLeftPart(UriPartial.Authority);
var img = new ImageValue
{
fileName = addedAttachment.FileName,
serverUrl = origin,
serverRelativeUrl = addedAttachment.ServerRelativeUrl
};
string json = JsonConvert.SerializeObject(img);
// 3) Image 列へ反映
item[imageColumnInternalName] = json;
item.Update();
ctx.ExecuteQuery();
}
// (参考)新規作成アイテムに対して行う場合の骨組み
public static int CreateItemThenAttachAndSetImageColumn(
ClientContext ctx,
string listTitle,
string imageColumnInternalName,
string localJpgPath)
{
var list = ctx.Web.Lists.GetByTitle(listTitle);
// A) 先にアイテムを作って ID を確定
var itemCreateInfo = new ListItemCreationInformation();
var newItem = list.AddItem(itemCreateInfo);
// 例:タイトルを入れる(必要に応じて)
newItem["Title"] = "Created by CSOM";
newItem.Update();
ctx.ExecuteQuery();
int newId = newItem.Id;
// B) 確定した ID に対して「添付→Image 列反映」
UploadJpgAsAttachmentAndSetImageColumn(ctx, listTitle, newId, imageColumnInternalName, localJpgPath);
return newId;
}
}
添付ファイル方式のメリット・デメリット
| 項目 | メリット | デメリット |
|---|---|---|
| 保存場所 | アイテムに紐づき、データがまとまりやすい | 添付が無効だと使えない/ライブラリより運用機能が少ない |
| URL の安定性 | Attachments/<ItemId>/ の形で分かりやすい | アイテム ID が必要で、処理が複数ステップになりやすい |
| UI 編集との相性 | 「保存場所」だけは UI と同じに寄せられる | 環境によっては UI 上で再編集・置換が期待通り動かないことがある |
「UI が作る JSON(数字入りファイル名)」を無理に再現しない方がいい理由
モダン UI は内部的に、ファイル名にランダム文字列や数値を付けたり、追加のメタ情報(サムネイル用途の情報など)を JSON に持たせたりすることがあります。これを目視で “同じ形にしよう” としても、次の理由で安定しません。
- 公式に固定仕様として公開されていない部分が混ざりやすい(変更されても検知が難しい)
- 同じサイトでも列の設定や UX 更新で値の形が変わる可能性がある
- 「表示に必要な最小要件」より多くを再現しようとして壊れやすくなる
そのため、開発側は「自分がコントロールできる場所(ライブラリ or 添付)」へ画像を置き、その URL を参照する最小限の JSONを Image 列へ入れる、という方針が実務では成功しやすいです。
“UI 上で後から編集できない” ときの考え方と回避策
質問の文脈でもよく出てくるのが「添付ファイルとしてアップロードした画像は、後で UI から Image 列の編集ができない(または期待通りに差し替えられない)」というケースです。これには環境依存の要素があり、確実な万能解は作りづらいのが正直なところです。
ただし運用上の回避策は用意できます。
回避策A:編集可能性を重視するなら “ライブラリ保存” を採用する
- 画像をドキュメント ライブラリに置けば、SharePoint の標準機能(バージョン、権限、プレビュー等)を活用できます。
- Image 列は「参照先 URL」と割り切り、差し替え時はライブラリ側のファイルを置換(同名上書き)する設計にすると、一覧表示の見た目を変えずに更新できます。
回避策B:添付ファイル方式なら “置換は CSOM でやる” と割り切る
- UI 編集が不安定な場合でも、CSOM なら「既存添付を削除 → 新しい添付を追加 → Image 列更新」ができます。
- 運用ルールとして「画像の差し替えはシステム(バッチ)経由で行う」を決めると、ユーザーが迷いにくくなります。
回避策C:UI 編集の要件が強いなら “設計段階で検証項目に入れる”
- 本番テナント/同等環境で「UI から Image 列編集が可能か」「添付ファイル方式で差し替えできるか」は事前に検証してください。
- 同じ Microsoft 365 でも、機能の展開タイミングやリリースリングで挙動が変わることがあります。
よくあるハマりどころ(トラブルシューティング)
最後に、CSOM で Image 列を扱うときに現場で起きやすいトラブルと、切り分けの方向性を表にまとめます。
| 症状 | 原因の候補 | 対処 |
|---|---|---|
| 画像が表示されず、空欄になる | JSON のキー名が違う/文字列として入っていない | fileName, serverUrl, serverRelativeUrl のスペル・大小・キャメルケースを確認。CSOM では JSON “文字列” をセット。 |
| 壊れた画像(読み込みエラー)になる | 参照先 URL が間違っている/権限がない | serverRelativeUrl をブラウザーで直接開いて表示できるか確認。閲覧者権限で画像にアクセスできるかも確認。 |
| スペースを含むパスで失敗する | URL エンコード不足 | 原則、CSOM が返す ServerRelativeUrl をそのまま使う。自前生成する場合はスペースを %20 に。 |
| 添付ファイル追加で例外になる | リストで添付が無効 | list.EnableAttachments を確認し、有効化するか、ライブラリ保存方式へ切替。 |
| 新規作成で添付がうまくいかない | アイテム ID 未確定 | 先にアイテムを作成し ExecuteQuery() して Id を確定させてから添付を追加。 |
| 列にセットしたのに一覧に反映されない | 内部名の取り違え/列の種類が違う | 列の内部名を再確認。Image 列以外(ハイパーリンク、複数行テキスト等)に JSON を入れていないか確認。 |
運用で迷わないためのおすすめ設計
最後に、実装だけでなく運用面で失敗しにくい判断軸をまとめます。
- 安定性重視:画像はドキュメント ライブラリに集約し、Image 列は参照(URL)として使う
- アイテム完結重視:添付ファイルとして保持し、差し替えは CSOM/バッチで統制する
- 権限設計:「一覧を見る人」が画像にもアクセスできる権限を必ず担保する(表示できない原因の多くは権限)
- 命名規則:衝突回避のために
GUIDやタイムスタンプを含める(例:img_20251221_101530.jpg) - 仕様変更耐性:UI の内部 JSON を完全再現するより、最小 JSON を安定運用する
まとめ
SharePoint Online の Image 列に CSOM(C#) で jpg を表示させるコツは、「列に画像データを入れる」のではなく、画像ファイルをどこかにアップロードして、その参照情報を JSON で列に保存するという考え方に切り替えることです。
- 最も堅実なのは、画像をライブラリにアップロードして URL を JSON でセットする方法
- UI と同じ場所に寄せたいなら、添付ファイルとしてアップロード → 添付の URL を JSON に入れる方法
- UI が生成する “数字入り” の値を無理に再現せず、最小構成の JSON で安定運用するのが実務向き

コメント