上载大文件示例 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 - 文件小于 10 MB 时,使用 SharePoint Online。 |
SharePoint Server、SharePoint Online |
通过 File 类上的 StartUpload 、ContinueUpload 和 FinishUpload 方法,将单个文件作为一组区块进行上传。 |
没有文件大小限制。 30 分钟后将发生超时。 每个文件块都必须在前一个文件块完成后 30 分钟内上载,以免发生超时。 | 当文件大于 10 MB 时,建议用于 SharePoint Online。 | SharePoint Online |
准备工作
若要开始,请从 GitHub 上的 Office 365 开发人员模式和做法项目下载 Core.LargeFileUpload 示例外接程序。
注意
本文中的代码按原样提供,不提供任何明示或暗示的担保,包括对特定用途适用性、适销性或不侵权的默示担保。
使用 Core.LargeFileUpload 示例外接程序
启动此代码示例时,将显示控制台应用程序。 你必须为 Office 365 提供 SharePoint Online 网站集 URL 和你的登录凭据。
身份验证后,控制台应用程序显示异常。 该异常是在 FileUploadService.cs 中的 UploadDocumentContent
方法尝试使用 FileCreationInformation.Content
属性上传大于 2 MB 的文件时发生的。 UploadDocumentContent
还会创建一个名为 Docs 的文档库(如果尚未存在)。 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 中,此代码示例提供了三种可用于将大文件上传到文档库的方法:
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
大于区块大小:- 文件区块将读取到
buffer
中。 - 如果区块大小等于文件大小,则读取整个文件。 区块将复制到
lastBuffer
。lastBuffer
File.FinishUpload
然后使用 上传区块。
- 文件区块将读取到
- 如果区块大小不等于文件大小,则会从该文件中读取多个区块。 调用
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”文档库中包含三个大文件。