OneDrive REST API - 上传块时请求的范围错误

OneDrive REST API - Requested Range Error While Uploading Chunk

我一直在尝试按照此文档 (https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online) 通过 HTTP 请求将文件上传到我的 OneDrive,但没有成功。我汇总了以下步骤 (Authentication, folder creation for the file, create an upload session) 但是当我尝试最后一步时,字节上传到创建的 session,我在第二个 PUT 请求中收到此错误:

请求范围不满足{"error":{"code":"invalidRange","message":"分片上传时乐观并发失败"}}

这是我的代码:

//Get File Data
byte[] FileByteArray = File.ReadAllBytes(FilePath);

//Create Upload Session
OutlookEndpoint = $"{AppSettings.DriveSettings.OneDriveSettings.Endpoint}/me/drive/items/{FolderId}:/{Name}:/createuploadsession";
OutlookResponseMessage = await OutlookClient.PostAsync(OutlookEndpoint, new StringContent("{}", Encoding.UTF8, "application/json"));
OutlookResponseContent = await OutlookResponseMessage.Content.ReadAsStringAsync();

if (OutlookResponseMessage.IsSuccessStatusCode)
{
    OutlookUpload OutlookUpload = JsonConvert.DeserializeObject<OutlookUpload>(OutlookResponseContent);

    //Check the Created URL
    if (!string.IsNullOrEmpty(OutlookUpload.UploadUrl))
    {
        //Chunk Calculation
        int TotalSize = FileByteArray.Length;
        int AcumulativeSize = 0;
        int ChunkSize = 327680;
        int ChunkBuffer = ChunkSize;
        int ChunkNumber = TotalSize / ChunkSize;
        int ChunkLeftover = TotalSize - ChunkSize * ChunkNumber;
        int ChunkCounter = 0;

        while (true)
        {
            if (ChunkNumber == ChunkCounter)
            {
                ChunkSize = ChunkLeftover;
            }

            byte[] ChunkData = FileByteArray.Skip(ChunkBuffer * ChunkCounter).Take(ChunkSize).ToArray();

            AcumulativeSize += ChunkData.Length;

            //PUT Upload of Chunk
            string UploadEndpoint = OutlookUpload.UploadUrl;

            string BytesHeader = $"bytes {AcumulativeSize - ChunkSize}-{AcumulativeSize - 1}/{TotalSize}";

            OutlookClient.DefaultRequestHeaders.Clear();
            OutlookClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
            OutlookClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Length", ChunkSize.ToString());
            OutlookClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Range", BytesHeader);

            OutlookResponseMessage = await OutlookClient.PutAsync(UploadEndpoint, new ByteArrayContent(ChunkData));
            OutlookResponseContent = await OutlookResponseMessage.Content.ReadAsStringAsync();

            if (OutlookResponseMessage.IsSuccessStatusCode)
            {
                Console.WriteLine("SUCCESS");
            }
            else
            {
                Console.WriteLine(OutlookResponseMessage.ReasonPhrase);
            }

            if (ChunkNumber == ChunkCounter)
            {
                break;
            }

            ChunkCounter++;
        }
    }
}

也许我遗漏了什么。我只在第一个 PUT 请求中收到 SUCCESS 消息,其他人总是给我上述错误。这是我发送的 headers 的错误图片。 Image

非常感谢您的帮助,感谢您阅读到这里。

编辑:

在修改请求的 header 配置并更改块的创建方式后,它开始工作了。

//Get File Data
byte[] FileByteArray = File.ReadAllBytes(FilePath);

//Create Upload Session
OutlookEndpoint = $"{AppSettings.DriveSettings.OneDriveSettings.Endpoint}/me/drive/items/{FolderId}:/{Name}:/createuploadsession";
OutlookResponseMessage = await OutlookClient.PostAsync(OutlookEndpoint, new StringContent("{}", Encoding.UTF8, "application/json"));
OutlookResponseContent = await OutlookResponseMessage.Content.ReadAsStringAsync();

if (OutlookResponseMessage.IsSuccessStatusCode)
{
    OutlookUpload OutlookUpload = JsonConvert.DeserializeObject<OutlookUpload>(OutlookResponseContent);

    //Check the Created URL
    if (!string.IsNullOrEmpty(OutlookUpload.UploadUrl))
    {
        using MemoryStream FileStream = new MemoryStream(FileByteArray);
        
        //Chunk Calculation
        int ChunkSize = 320 * 1024;
        int ChunkRemaining = 0;
        byte[] ByteBuffer = new byte[ChunkSize];
        int BytesRead = 0;
        
        while ((BytesRead = FileStream.Read(ByteBuffer, 0, ByteBuffer.Length)) > 0)
        {
            if (BytesRead < ChunkSize)
            {
                byte[] LastBuffer = new byte[BytesRead];

                Buffer.BlockCopy(ByteBuffer, 0, LastBuffer, 0, BytesRead);

                ByteBuffer = new byte[BytesRead];

                ByteBuffer = LastBuffer;
            }

            try
            {
                OutlookClient.DefaultRequestHeaders.Clear();

                string UploadEndpoint = OutlookUpload.UploadUrl;

                string BytesHeader = $"bytes {ChunkRemaining}-{ChunkRemaining + ByteBuffer.Length - 1}/{FileByteArray.Length}";

                HttpRequestMessage MicrosoftResponseMessage = new HttpRequestMessage()
                {
                    Content = new ByteArrayContent(ByteBuffer),
                    RequestUri = new Uri(UploadEndpoint),
                    Method = HttpMethod.Put,
                };

                MicrosoftResponseMessage.Content.Headers.Add("Content-Length", ByteBuffer.Length.ToString());

                MicrosoftResponseMessage.Content.Headers.Add("Content-Range", BytesHeader);

                OutlookResponseMessage = await OutlookClient.SendAsync(MicrosoftResponseMessage);
                
                OutlookResponseContent = await OutlookResponseMessage.Content.ReadAsStringAsync();

                if (OutlookResponseMessage.IsSuccessStatusCode)
                {
                    Console.WriteLine("SUCCESS");
                
                    ChunkRemaining += ByteBuffer.Length;
                    
                    if (ChunkRemaining == FileByteArray.Length)
                    {
                        Console.WriteLine("COMPLETED");
                    }
                }
                else
                {
                    Console.WriteLine(OutlookResponseMessage.ReasonPhrase);
                }
            }
            catch (Exception Exception)
            {
                Console.WriteLine(Exception.Message);

                break;
            }
        }
    }
}

请注意,如果客户端发送服务器已收到的片段失败,服务器将响应 HTTP 416 Requested Range Not Satisfiable。您可以 request upload status 获取更详细的缺失范围列表。显然 content-range 和 content-length 是问题所在。您将 header 配置从 HttpClient 更改为 HttpRequestMessage,它现在运行良好。