ResourcePath API 使用時にファイルとフォルダーで % と # をサポートする

% と # を含むファイルとフォルダーのサポートが SharePoint Online に展開されています。 残念ながら、既存の API の構造や呼び出しパターンのために、これらのファイル名が関係する作業はあいまいになることがあります。 開発者ブログで、この背後にある詳細な背景を紹介しています。

要約すると、# と % の文字のサポートを提供するために、SharePoint Online クライアント オブジェクト モデル (CSOM) サーフェスに新しい API が追加されました。

新しい ResourcePath クラス

要点をまとめると、既存の文字列ベースの SharePoint API (SPFileCollection.GetByUrl など) は、パスに含まれる % と # の文字は URL がエンコードされていることを暗黙的に示していると自動的に想定することによって、エンコードされた URL とデコードされた URL の両方を処理するということに注意してください。 ファイルとフォルダーで新たに % と # がサポートされるようになったことで、この自動処理は問題になることがあります。なぜなら、この処理によって、% と # が含まれるファイル名がダウンストリーム コードで無視されたり正しく処理されなかったりする可能性があるためです。

新しいクラス Microsoft.SharePoint.Client.ResourcePath が API に追加されました。 ResourcePath クラスは、これらの新しい文字のサポートに不可欠です。 これは、デコードされた URL を想定し、デコードされた URL のみを処理するため、SharePoint と OneDrive for Business 内のアイテムを扱ううえであいまいさのない新しい方法を提供します。 これは、サイト コレクション、サイト、ファイル、フォルダーまたはその他の成果物と OneDrive for Business の完全 (絶対) または部分 (相対) URL を表す文字列ベースのパスを置き換えるものです。 URL 内の % と # を適切にサポートするには、ResourcePath ベースの API をデコードされた URL と共に使用する必要があります。

ResourcePath は、静的関数 ResourcePath.FromDecodedUrl(string) を呼び出すことによって簡単に構築することができます。 渡された入力値には、プロパティ DecodedUrl を呼び出すことによって ResourcePath オブジェクトからアクセスできます。

文字列ベースの URL を使用する既存の呼び出しの場合、文字列ベースの URL 呼び出しにつながるコード パスがエンコードされた URL とデコードされた URL のどちらで提供されたものか、また、それらのコード パスがアンカー リンク (つまり、ファイル URL の先頭に #bookmark を追加) を許可していたかどうかを判別する必要があります。

注:

単純に検索と置換で既存の文字列ベースの URL API を ResourcePath.FromDecodedUrl に変更しないでください。 ResourcePath.FromDecodedUrl(string) API を使用する前に、適切に URL を判別し、該当する場合は URL をデコードしておく必要があります。

文字列ベースの SharePoint API 呼び出しにつながるコード パスがデコードされた URL ('FY17 Report.docx' など) を使用していた場合は、単純にそれらの呼び出しを ResourcePath.FromDecodedUrl(string) と後述する同等の ResourcePath メソッドに置き換えることができます。

API を介して SharePoint から URL を参照する多くのケースでは、ResourcePath はこれらのコード パターンをより一貫性のあるものにするためにも提供されていることに注意してください。これにより、ResourcePath を URL の代わりに使用することができます。

また、この変更は SharePoint の REST ベースの呼び出しにも適用されることにも注意してください。 REST 内のこれらの新しい API の例を確認するには、以下のシナリオを参照してください。

# と % をサポートする ResourcePath に基づく新しい API

次の表に、# と % をサポートするために既存の API を置き換えるものとして導入された新しい API を示します。 比較しやすいように、従来の API と新しい API を横に並べて一覧にしています。 これらの API のコア機能は変わりませんが、アイテムの位置を表す方法が変更されています。

新しい API の詳細については、「SharePoint Online の .NET クライアント API リファレンス」を参照してください。 以下に .NET CSOM API の一覧を記載しましたが、新しいメソッドは JavaScript CSOM ライブラリの同等の形式でも利用できます。

アセンブリ Microsoft.SharePoint.Client.dll

従来のメソッド 新しいメソッド
Microsoft.SharePoint.SPList AddItem(Microsoft.SharePoint.SPListItemCreationInformation) AddItemUsingPath(SPListItemCreationInformationUsingPath parameters)
Microsoft.SharePoint.SPFile MoveTo(System.String, Microsoft.SharePoint.SPMoveOperations) MoveToUsingPath(SPResourcePath newPath, SPMoveOperations moveOperations)
Microsoft.SharePoint.SPFile CopyTo(System.String, Boolean) CopyToUsingPath(SPResourcePath newPath, bool overwrite)
Microsoft.SharePoint.SPFileCollection GetByUrl(System.String) GetByPath(SPResourcePath)
Microsoft.SharePoint.SPFileCollection Add(System.String, Microsoft.SharePoint.SPTemplateFileType) AddUsingPath(SPResourcePath, SPFileCollectionAddWithTemplateParameters)
Microsoft.SharePoint.SPFolder MoveTo(System.String) MoveToUsingPath(SPResourcePath newPath)
Microsoft.SharePoint.SPFolder AddSubFolder(System.String) AddSubFolderUsingPath(SPResourcePath leafPath)
Microsoft.SharePoint.SPFolderCollection GetByUrl(System.String) GetByPath(SPResourcePath path)
Microsoft.SharePoint.SPFolderCollection Add(System.String) AddUsingPath(SPResourcePath path, SPFolderCreationInformation parameters)
AddWithOverwrite(string url, bool overwrite)
Microsoft.SharePoint.SPRemoteWeb GetFileByServerRelativeUrl(System.String) GetFileByServerRelativePath(SPResourcePath path)
Microsoft.SharePoint.SPWeb GetList(System.String) GetListUsingPath(SPResourcePath)
Microsoft.SharePoint.SPWeb GetListItem(System.String) GetListItemUsingPath(SPResourcePath)

次の CSOM オブジェクトは、これらの API で使用可能な ResourcePath プロパティを返します。 従来のプロパティもデコードされた URL を返しますが、これらの API を呼び出す際の利便性、シンプルさとわかりやすさのために、新しい ResourcePath プロパティが提供されます。 1 つの例外は、SPFolder.WelcomePage プロパティです。これは、以前はエンコードされた URL とエンコードされていない URL の両方を返しましたが、WelcomePagePath プロパティを介して明確に返されるようになりました。


種類 従来のプロパティ 新しいプロパティ
Microsoft.SharePoint.SPList DefaultViewUrl DefaultViewPath
Microsoft.SharePoint.SPList DefaultEditFormUrl DefaultEditFormPath
Microsoft.SharePoint.SPList DefaultNewFormUrl DefaultNewFormPath
Microsoft.SharePoint.SPList DefaultDisplayFormUrl DefaultDisplayFormPath
Microsoft.SharePoint.SPAttachment ServerRelativeUrl ServerRelativePath
Microsoft.SharePoint.SPFile ServerRelativeUrl ServerRelativePath
Microsoft.SharePoint.SPFolder ServerRelativeUrl ServerRelativePath
Microsoft.SharePoint.SPFolder WelcomePage WelcomePagePath/ WelcomePageParameters
Microsoft.SharePoint.SPView ServerRelativeUrl ServerRelativePath
Microsoft.SharePoint.SPDocumentLibraryInformation ServerRelativeUrl ServerRelativePath

URL の形式についてあいまいでなく、# と % をサポートできる既存の API

次の API は、入力として適切にエンコードされた URL のみを受け入れます。 URL があいまいさなしで使用できる限り、エンコードが十分でない URL もサポートします。 つまり、URL のパス内の少なくとも # または % 記号が % エンコードされている必要があります。 これらの API は、引き続き現在と同じように機能します。 URL 内の # は、URL パスの一部としてではなく、フラグメント区切り記号として扱われます。

従来のプロパティ
Microsoft.SharePoint.SPWeb GetFileByUrl(System.String)
Microsoft.SharePoint.SPWeb GetFileByWOPIFrameUrl(System.String)
Microsoft.SharePoint.SPWeb GetFileByLinkingUrl(System.String)

上記の API で使用するための、あいまいさのないエンコードの System.Uri を返すために、次の C# プロパティが追加されました。 次の API の以前のバリアントは、デコードされた URL を返しました。それらの URL には # または % 記号が含まれることはなかったため、URL はあいまいになりませんでした。 将来的に、パス内の # と % の文字をエンコードする、以前のバリアントのデコードの動作を中止したくはありませんでした。 したがって、新しい API が作成されました。

種類 従来のプロパティ 新しいプロパティ
Microsoft.SharePoint.SPFile LinkingUrl LinkingUri
Microsoft.SharePoint.SPFile ServerRedirectedEmbedUrl ServerRedirectedEmbedUri

サンプル コード

CSOM シナリオ

フォルダーへのファイルの追加 (.net)

 ClientContext context = new ClientContext("http://site");
 Web web = context.Web;
 // Get the parent folder
 ResourcePath folderPath = ResourcePath.FromDecodedUrl("/Shared Documents");
 Folder parentFolder = web.GetFolderByServerRelativePath(folderPath);
 
 // Create the parameters used to add a file
 ResourcePath filePath = ResourcePath.FromDecodedUrl("/Shared Documents/hello world.txt");
 byte[] fileContent = System.Text.Encoding.UTF8.GetBytes("sample file content");
 FileCollectionAddParameters fileAddParameters = new FileCollectionAddParameters();
 fileAddParameters.Overwrite = true;
 using (MemoryStream contentStream = new MemoryStream(fileContent))
 {
  // Add a file
  Microsoft.SharePoint.Client.File addedFile = parentFolder.Files.AddUsingPath(filePath, fileAddParameters, contentStream);
 
  // Select properties of added file to inspect
  context.Load(addedFile, f => f.UniqueId, f1 => f1.ServerRelativePath);
 
  // Perform the actual operation
  context.ExecuteQuery();
 
  // Print the results
  Console.WriteLine(
   "Added File [UniqueId:{0}] [ServerRelativePath:{1}]",
   addedFile.UniqueId,
   addedFile.ServerRelativePath.DecodedUrl);
 }


フォルダーへのサブフォルダーの追加 (.net)

  ClientContext context = new ClientContext("http://site");
  Web web = context.Web;
  // Get the parent folder
  ResourcePath folderPath = ResourcePath.FromDecodedUrl("Shared Documents");
  Folder parentFolder = web.GetFolderByServerRelativePath(folderPath);
 
  // Create the parameters used to add a folder
  ResourcePath subFolderPath = ResourcePath.FromDecodedUrl("Shared Documents/sub folder");
  FolderCollectionAddParameters folderAddParameters = new FolderCollectionAddParameters();
  folderAddParameters.Overwrite = true;
 
  // Add a sub folder
  Folder addedFolder = parentFolder.Folders.AddUsingPath(subFolderPath, folderAddParameters);
 
  // Select properties of added file to inspect
  context.Load(addedFolder, f => f.UniqueId, f1 => f1.ServerRelativePath);
 
  // Perform the actual operation
  context.ExecuteQuery();
 
  // Print the results
  Console.WriteLine(
    "Added Folder [UniqueId:{0}] [ServerRelativePath:{1}]",
    addedFolder.UniqueId,
    addedFolder.ServerRelativePath.DecodedUrl);

Web にあるファイルの取得 (.net)

  ClientContext context = new ClientContext("http://site");
  Web web = context.Web;
  // Get the file
  ResourcePath filePath = ResourcePath.FromDecodedUrl("/Shared Documents/hello world.txt");
  File file = web.GetFileByServerRelativePath(filePath);
 
  // Select properties of the file
  context.Load(file, f => f.UniqueId, f1 => f1.ServerRelativePath);
 
  // Perform the actual operation
  context.ExecuteQuery();
 
  // Print the results
  Console.WriteLine(
    "File Properties [UniqueId:{0}] [ServerRelativePath:{1}]",
    file.UniqueId,
    file.ServerRelativePath.DecodedUrl);

REST シナリオ

フォルダーの取得

url: http://site url/_api/web/GetFolderByServerRelativePath(decodedUrl='library name/folder name')
method: GET
headers:
  Authorization: "Bearer " + accessToken
  accept: "application/json;odata=verbose" or "application/atom+xml"


フォルダーの作成

url: http://site url/_api/web/Folders/AddUsingPath(decodedurl='/document library relative url/folder name')
method: POST
headers:
  Authorization: "Bearer " + accessToken
  X-RequestDigest: form digest value
  accept: "application/json;odata=verbose"
  content-type: "application/json;odata=verbose"
 

ファイルの取得

url: http://site url/_api/web/GetFileByServerRelativePath(decodedUrl='folder name/file name')
method: GET
Headers:
  Authorization: "Bearer " + accessToken
  accept: "application/json;odata=verbose" or "application/atom+xml"


ファイルの追加

url: http://site url/_api/web/GetFolderByServerRelativePath(decodedUrl='folder name')/Files/AddStubUsingPath(decodedurl='testfile.txt')
methods: POST
Headers:
  Authorization: "Bearer " + accessToken
  X-RequestDigest: form digest value
  accept: "application/json;odata=verbose"
  content-type: "application/json;odata=verbose"
  Content-Length: length
 

関連項目