如何在外部.NET Web应用中用Microsoft Graph上传大文件至文档库?
使用Microsoft Graph分块上传大文件到文档库
我完全懂你的痛点——小文件能顺利上传,但超过4MB就报错,这是因为Graph的直接/content上传接口默认有4MB的大小限制。而createUploadSession就是微软官方提供的大文件分块上传解决方案,我给你一步步拆解实现,结合你现有的.NET HttpClient代码来调整:
核心思路
分块上传分为两步:先创建一个上传会话(拿到专属的上传URL),然后把大文件拆成多个小块,依次上传到这个会话URL,最后完成上传。
第一步:创建上传会话
这一步的目的是获取一个临时的上传会话URL,后续所有分块都通过这个URL上传,同时可以指定文件的冲突处理策略(比如覆盖已存在的文件)。
代码示例
using System; using System.IO; using System.Net.Http; using System.Net.Http.Headers; using System.Text; using Newtonsoft.Json; // 替换成你的实际参数 string driveId = "{mydriveid}"; string localFilePath = @"C:\你的大文件路径\bigfile.zip"; string fileName = Path.GetFileName(localFilePath); long totalFileSize = new FileInfo(localFilePath).Length; // 1. 构建创建会话的请求 string createSessionUrl = $"https://graph.microsoft.com/v1.0/drives/{driveId}/items/root:/{fileName}:/createUploadSession"; // 请求体:指定冲突策略和文件名 var sessionRequestBody = new { item = new { @microsoft.graph.conflictBehavior = "replace", // 可选值:replace/rename/fail name = fileName } }; using (HttpClient client = new HttpClient()) { // 记得添加你的Bearer令牌 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "你的访问令牌"); var content = new StringContent(JsonConvert.SerializeObject(sessionRequestBody), Encoding.UTF8, "application/json"); var response = await client.PostAsync(createSessionUrl, content); response.EnsureSuccessStatusCode(); // 处理请求失败的情况 // 解析响应,拿到上传会话URL var sessionResponse = await response.Content.ReadAsAsync<dynamic>(); string uploadSessionUrl = sessionResponse.uploadUrl; // 下一步:用这个uploadSessionUrl进行分块上传 await UploadFileInChunks(uploadSessionUrl, localFilePath, totalFileSize); }
第二步:分块上传文件内容
微软推荐的分块大小是5MB到100MB,最小不能小于5MB(最后一块除外)。我们需要读取文件的字节流,拆分后逐个上传,同时通过Content-Range请求头告诉Graph当前块的范围。
分块上传的方法实现
private static async Task UploadFileInChunks(string uploadSessionUrl, string localFilePath, long totalFileSize) { const int chunkSize = 5 * 1024 * 1024; // 5MB分块 byte[] buffer = new byte[chunkSize]; long uploadedBytes = 0; using (FileStream fileStream = new FileStream(localFilePath, FileMode.Open, FileAccess.Read)) { using (HttpClient client = new HttpClient()) { while (uploadedBytes < totalFileSize) { int bytesRead = await fileStream.ReadAsync(buffer, 0, chunkSize); if (bytesRead == 0) break; // 计算当前块的字节范围 long startByte = uploadedBytes; long endByte = uploadedBytes + bytesRead - 1; string contentRangeHeader = $"bytes {startByte}-{endByte}/{totalFileSize}"; // 构建分块的请求内容 var chunkContent = new ByteArrayContent(buffer, 0, bytesRead); chunkContent.Headers.ContentRange = new ContentRangeHeaderValue(startByte, endByte, totalFileSize); // 发送PUT请求到会话URL var chunkResponse = await client.PutAsync(uploadSessionUrl, chunkContent); if (chunkResponse.StatusCode == System.Net.HttpStatusCode.Created) { // 上传完成,返回文件的详细信息 var uploadedFile = await chunkResponse.Content.ReadAsAsync<dynamic>(); Console.WriteLine($"大文件上传成功!文件ID:{uploadedFile.id}"); break; } else if (chunkResponse.StatusCode == System.Net.HttpStatusCode.Accepted) { // 继续上传下一块,更新已上传字节数 var sessionStatus = await chunkResponse.Content.ReadAsAsync<dynamic>(); uploadedBytes = long.Parse(sessionStatus.nextExpectedRanges[0].Split('-')[0]); } else { // 处理上传错误 chunkResponse.EnsureSuccessStatusCode(); } } } } }
关键注意事项
- 权限要求:你的访问令牌需要拥有
Files.ReadWrite(OneDrive)或Sites.ReadWrite.All(SharePoint文档库)的权限 - 会话有效期:上传会话默认有效期是24小时,如果上传中断,可以复用这个会话继续上传未完成的块
- 分块大小:不要设置小于5MB的分块(最后一块除外),否则会报错
- 冲突处理:根据业务需求调整
@microsoft.graph.conflictBehavior的值,比如rename会自动给重复文件名加后缀,fail会直接返回错误
内容的提问来源于stack exchange,提问作者Binesh
相关产品推荐
相关产品推荐

