大きいファイルをアップロードするサンプル アドイン (SharePoint)
Core.LargeFileUpload サンプルは、プロバイダー ホスト型アドインを使用して大きなファイルを SharePoint にアップロードする方法と、ファイル アップロードの 2 MB サイズ制限を回避する方法を示しています。
2 MB を超えるファイルを SharePoint にアップロードするには、この解決策を使用してください。
このサンプルは、コンソール アプリケーションとして稼働し、次のいずれかのメソッドを使って大きなファイルをドキュメント ライブラリにアップロードします。
- Microsoft.SharePoint.Client.File クラスの SaveBinaryDirect メソッド。
- FileCreationInformation クラスの ContentStream プロパティ。
- File クラスの StartUpload、ContinueUpload、および FinishUpload メソッド。
次の表に、ファイルのアップロード方法として使用できるオプションと、それぞれのオプションを使用する状況を記載します。
ファイルをアップロードする場合のオプション
ファイルをアップロードする方法 | 考慮事項 | いつ使用するか? | サポートされるプラットフォーム |
---|---|---|---|
FileCreationInformation クラスの Content プロパティ。 | アップロードできるファイルの最大サイズは 2 MB です。 30 分後にタイムアウトが発生します。 | 2 MB 以下のファイルをアップロードする場合にのみ使用します。 | SharePoint Server、SharePoint Online |
File クラスの SaveBinaryDirect メソッド。 |
ファイル サイズの制限はありません。 30 分後にタイムアウトが発生します。 | この方法を使用するのは、ユーザー用の認証ポリシーを使用している場合に限られます。 ユーザー用の認証ポリシーは SharePoint アドインでは使用できませんが、ネイティブ デバイス アドイン、Windows PowerShell、Windows コンソール アプリケーションでは使用できます。 | SharePoint Server、SharePoint Online |
FileCreationInformation クラスの ContentStream プロパティ。 |
ファイル サイズの制限はありません。 30 分後にタイムアウトが発生します。 | 推奨される場合: - SharePoint Server - SharePoint Online (ファイルのサイズが 10 MB 未満の場合) |
SharePoint Server、SharePoint Online |
File クラスの StartUpload メソッド、ContinueUpload メソッド、および FinishUpload メソッドを使用して、1 つのファイルを一連のチャンクとしてアップロードします。 |
ファイル サイズの制限はありません。 30 分後にタイムアウトが発生します。 タイムアウトを避けるため、ファイルの各チャンクは、その前のチャンクの完了後 30 分以内にアップロードする必要があります。 | ファイルが 10 MB より大きい場合、SharePoint Online に対して推奨。 | SharePoint Online |
はじめに
まず、Core.LargeFileUpload サンプル アドインを GitHub 上の Office 365 Developer Patterns and Practices プロジェクトからダウンロードします。
注:
この記事で提供されるコードは、明示または黙示のいかなる種類の保証なしに現状のまま提供されるものであり、特定目的への適合性、商品性、権利侵害の不存在についての暗黙的な保証は一切ありません。
Core.LargeFileUpload サンプル アドインを使用する
このコード サンプルを起動すると、コンソール アプリケーションが表示されます。 SharePoint Online サイト コレクションの URL と、Office 365 のサインイン認証情報を入力する必要があります。
認証後、コンソール アプリケーションには例外が表示されます。 この例外は、FileUploadService.cs の UploadDocumentContent
メソッドが FileCreationInformation.Content
プロパティを使用して 2 MB を超えるファイルをアップロードしようとすると発生します。 また、Docs と呼ばれるドキュメント ライブラリがまだ存在していない場合は、UploadDocumentContent
によってそれが作成されます。 Docs ドキュメント ライブラリは、後で、このコード サンプルの中で使用されます。
public void UploadDocumentContent(ClientContext ctx, string libraryName, string filePath)
{
Web web = ctx.Web;
// Ensure that target library exists. Create if it is missing.
if (!LibraryExists(ctx, web, libraryName))
{
CreateLibrary(ctx, web, libraryName);
}
FileCreationInformation newFile = new FileCreationInformation();
// The next line of code causes an exception to be thrown for files larger than 2 MB.
newFile.Content = System.IO.File.ReadAllBytes(filePath);
newFile.Url = System.IO.Path.GetFileName(filePath);
// Get instances to the given library.
List docs = web.Lists.GetByTitle(libraryName);
// Add file to the library.
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(newFile);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
}
FileUploadService.cs のコード サンプルでは、ドキュメント ライブラリに大きなファイルをアップロードするために使用できる次の 3 つのオプションが示されています。
File.SaveBinaryDirect
メソッド。FileCreationInformation.ContentStream
プロパティ。- File クラスの
StartUpload
メソッド、ContinueUpload
メソッド、およびFinishUpload
メソッド。
FileUploadService.cs 内の SaveBinaryDirect
は、Microsoft.SharePoint.Client.File.SaveBinaryDirect
メソッドと FileStream
オブジェクトを使用して、ファイルをドキュメント ライブラリにアップロードします。
public void SaveBinaryDirect(ClientContext ctx, string libraryName, string filePath)
{
Web web = ctx.Web;
// Ensure that the target library exists. Create it if it is missing.
if (!LibraryExists(ctx, web, libraryName))
{
CreateLibrary(ctx, web, libraryName);
}
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
Microsoft.SharePoint.Client.File.SaveBinaryDirect(ctx, string.Format("/{0}/{1}", libraryName, System.IO.Path.GetFileName(filePath)), fs, true);
}
}
FileUploadService.cs 内の UploadDocumentContentStream
は、FileCreationInformation.ContentStream
プロパティと FileStream
オブジェクトを使用して、ファイルをドキュメント ライブラリにアップロードします。
public void UploadDocumentContentStream(ClientContext ctx, string libraryName, string filePath)
{
Web web = ctx.Web;
// Ensure that the target library exists. Create it if it is missing.
if (!LibraryExists(ctx, web, libraryName))
{
CreateLibrary(ctx, web, libraryName);
}
using (FileStream fs = new FileStream(filePath, FileMode.Open))
{
FileCreationInformation flciNewFile = new FileCreationInformation();
// This is the key difference for the first case - using ContentStream property
flciNewFile.ContentStream = fs;
flciNewFile.Url = System.IO.Path.GetFileName(filePath);
flciNewFile.Overwrite = true;
List docs = web.Lists.GetByTitle(libraryName);
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(flciNewFile);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
}
}
FileUploadService.cs で、UploadFileSlicePerSlice
大きなファイルを一連のチャンクまたはフラグメントとしてドキュメント ライブラリにアップロードします。 UploadFileSlicePerSlice
は、次のタスクを実行します。
- 新しい GUID を取得します。 ファイルを複数のチャンクに分割してアップロードするには、固有の GUID を使用する必要があります。
- チャンクのブロック サイズ (バイト数) を計算します。 ブロック サイズをバイト単位で計算するには、
UploadFileSlicePerSlice
を使用fileChunkSizeInMB
します。これは、個々のチャンクのサイズを MB 単位で指定します。 - アップロードするファイルのサイズ (
fileSize
) が、チャンクのサイズ (blockSize
) 以下かどうかを調べます。fileSize
がチャンク サイズ以下の場合でも、このサンプルでは、FileCreationInformation.ContentStream
プロパティを使用することによりファイルがアップロードされるようになっています。 推奨されるチャンク サイズは、10 MB 以上です。fileSize
がチャンク サイズより大きい場合、- ファイルの 1 個のチャンクが
buffer
に読み込まれます。 - チャンク サイズがファイル サイズと等しい場合は、ファイル全体が読み込まれます。 チャンクは に
lastBuffer
コピーされます。lastBuffer
を使用File.FinishUpload
してチャンクをアップロードします。
- ファイルの 1 個のチャンクが
- チャンク サイズがファイル サイズと等しくない場合は、ファイルから複数のチャンクが読み取られます。 最初のチャンクをアップロードするために、
File.StartUpload
が呼び出されます。 続いて、次のチャンクの開始点として使用されるfileoffset
が、最初のチャンクからアップロードされたバイト数に設定されます。 次のチャンクが読み取られたときに、最後のチャンクにまだ達していない場合、ファイルの次のチャンクをアップロードするためにFile.ContinueUpload
が呼び出されます。 最後のチャンクが読み取られるまで、このプロセスが繰り返されます。 最後のチャンクが読み取られる際は、File.FinishUpload
が最後のチャンクをアップロードして、ファイルをコミットします。 この方法が完了した時点で、ファイルの内容が変更されます。
注:
以下のベスト プラクティスについて考慮してください。
- アップロードが中断した場合に備えて、再試行メカニズムを使用してください。 ファイルのアップロードが中断された場合、そのファイルは未完ファイルと呼ばれます。 未完ファイルのアップロードは、アップロード中断の後、すぐにでも再開できます。 未完ファイルの中断後、6 時間から 24 時間経過すると、未完ファイルはサーバーから削除されます。 この削除期間は予告なく変更されることがあります。
- ファイルをチャンクで SharePoint Online にアップロードする際は、SharePoint Online でファイルがロックされます。 中断が発生した場合、15 分間にわたりファイルがロックされたままになります。 ファイルの次のチャンクが 15 分以内に SharePoint Online にアップロードされなければ、ロックが解除されます。 ロックが解除された後、アップロードを再開するか、別のユーザーがファイルのアップロードを開始できます。 別のユーザーがファイルのアップロードを開始すると、アップロード未完了のファイルは SharePoint Online から削除されます。 アップロードが中断された後にファイルのロック状態が維持される期間は、予告なく変更されることがあります。
- チャンク サイズを変更する場合があります。 チャンク サイズは 10 MB を使用することをお勧めします。
- 正常にアップロードされたチャンクを追跡して、中断されたチャンクを再開します。
チャンクは、順番にアップロードする必要があります。 スライスを同時にアップロードすることはできません (たとえば、マルチスレッド アプローチを使用)。
public Microsoft.SharePoint.Client.File UploadFileSlicePerSlice(ClientContext ctx, string libraryName, string fileName, int fileChunkSizeInMB = 3)
{
// Each sliced upload requires a unique ID.
Guid uploadId = Guid.NewGuid();
// Get the name of the file.
string uniqueFileName = Path.GetFileName(fileName);
// Ensure that target library exists, and create it if it is missing.
if (!LibraryExists(ctx, ctx.Web, libraryName))
{
CreateLibrary(ctx, ctx.Web, libraryName);
}
// Get the folder to upload into.
List docs = ctx.Web.Lists.GetByTitle(libraryName);
ctx.Load(docs, l => l.RootFolder);
// Get the information about the folder that will hold the file.
ctx.Load(docs.RootFolder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
// File object.
Microsoft.SharePoint.Client.File uploadFile = null;
// Calculate block size in bytes.
int blockSize = fileChunkSizeInMB * 1024 * 1024;
// Get the information about the folder that will hold the file.
ctx.Load(docs.RootFolder, f => f.ServerRelativeUrl);
ctx.ExecuteQuery();
// Get the size of the file.
long fileSize = new FileInfo(fileName).Length;
if (fileSize <= blockSize)
{
// Use regular approach.
using (FileStream fs = new FileStream(fileName, FileMode.Open))
{
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = fs;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = docs.RootFolder.Files.Add(fileInfo);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
// Use large file upload approach.
ClientResult<long> bytesUploaded = null;
FileStream fs = null;
try
{
fs = System.IO.File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using (BinaryReader br = new BinaryReader(fs))
{
byte[] buffer = new byte[blockSize];
Byte[] lastBuffer = null;
long fileoffset = 0;
long totalBytesRead = 0;
int bytesRead;
bool first = true;
bool last = false;
// Read data from file system in blocks.
while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytesRead = totalBytesRead + bytesRead;
// You've reached the end of the file.
if (totalBytesRead == fileSize)
{
last = true;
// Copy to a new buffer that has the correct size.
lastBuffer = new byte[bytesRead];
Array.Copy(buffer, 0, lastBuffer, 0, bytesRead);
}
if (first)
{
using (MemoryStream contentStream = new MemoryStream())
{
// Add an empty file.
FileCreationInformation fileInfo = new FileCreationInformation();
fileInfo.ContentStream = contentStream;
fileInfo.Url = uniqueFileName;
fileInfo.Overwrite = true;
uploadFile = docs.RootFolder.Files.Add(fileInfo);
// Start upload by uploading the first slice.
using (MemoryStream s = new MemoryStream(buffer))
{
// Call the start upload method on the first slice.
bytesUploaded = uploadFile.StartUpload(uploadId, s);
ctx.ExecuteQuery();
// fileoffset is the pointer where the next slice will be added.
fileoffset = bytesUploaded.Value;
}
// You can only start the upload once.
first = false;
}
}
else
{
if (last)
{
// Is this the last slice of data?
using (MemoryStream s = new MemoryStream(lastBuffer))
{
// End sliced upload by calling FinishUpload.
uploadFile = uploadFile.FinishUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Return the file object for the uploaded file.
return uploadFile;
}
}
else
{
using (MemoryStream s = new MemoryStream(buffer))
{
// Continue sliced upload.
bytesUploaded = uploadFile.ContinueUpload(uploadId, fileoffset, s);
ctx.ExecuteQuery();
// Update fileoffset for the next slice.
fileoffset = bytesUploaded.Value;
}
}
}
} // while ((bytesRead = br.Read(buffer, 0, buffer.Length)) > 0)
}
}
finally
{
if (fs != null)
{
fs.Dispose();
}
}
}
return null;
}
コード サンプルが完了した後は、Office 365 サイトで [最近使用したファイル]>[Docs] を選択することによって、Docs ドキュメント ライブラリに移動できます。Docs ドキュメント ライブラリの中に、3 個の大きなファイルが含まれていることを確認してください。