Ошибка Диапазона, Запрошенная API REST OneDrive, При Загрузке Фрагмента

#c# #asp.net-core #microsoft-graph-api #httprequest #onedrive

Вопрос:

Я пытался загрузить файлы на свой OneDrive с помощью HTTP-запросов, следующих за этим документом (https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online) безуспешно. У меня округлены следующие шаги (аутентификация, создание папки для файла, создание сеанса загрузки), но когда я пытаюсь выполнить последний шаг, загрузить байт в созданный сеанс, я получаю эту ошибку во втором запросе PUT:

Запрошенный диапазон не удовлетворяется {«ошибка»:{«код»:»Недопустимый диапазон»,»сообщение»:»Сбой оптимистического параллелизма во время фрагментированной загрузки»}}

Это мой код:

 //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, остальные всегда выдают мне ошибку, описанную выше. Вот изображение ошибки с заголовками, которые я отправляю. Изображение

Я был бы признателен за любую помощь, спасибо, что дочитали до этого.

Редактировать:

Заставил его работать после изменения конфигурации заголовка для запроса и изменения способа создания фрагментов.

 //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;
            }
        }
    }
}
 

Комментарии:

1. Итак, у вас есть неудача выше. Пожалуйста, обратите внимание, что при сбоях, когда клиент отправил фрагмент, который сервер уже получил, сервер ответит, что запрошенный диапазон HTTP 416 Не удовлетворяется. Вы можете запросить статус загрузки , чтобы получить более подробный список отсутствующих диапазонов. Попробуйте и дайте нам знать, как это происходит.

2. Хорошо, я попробую изменить способ создания фрагментов, возможно, пропуск и взятие, которые я использую, неадекватны. Скоро обновится, спасибо @Dev.

3. Добро пожаловать @Диего. Дайте мне знать, если это поможет.

4. Спасибо @Dev, я изменил способ сборки фрагментов, но ошибка сохраняется, но я думаю, что знаю, что происходит сейчас. Когда я запрашиваю статус загрузки сеанса после отправки первого фрагмента, он выдает ошибку «Сеанс загрузки не найден». Ответ на первый запрос PUT дает мне объект item вместо ожидаемого объекта со значением «nextExpectedRanges». Я полагаю, что он думает, что я отправляю все данные одним куском, и завершает сеанс после первого ввода. Я попытался указать размер файла в запросе на создание сеанса, но все равно безуспешно. Вы знаете, почему это может произойти?

5. Неважно, нашел его! Очевидно, проблема заключалась в диапазоне и длине контента. Изменил конфигурацию заголовка с HttpClient на HttpRequestMessage, и теперь он отлично работал. Большое спасибо @Dev, я скоро обновлю исходный пост рабочим кодом.

Ответ №1:

Пожалуйста, обратите внимание, что при сбоях, когда клиент отправил фрагмент, который сервер уже получил, сервер ответит, что запрошенный диапазон HTTP 416 Не удовлетворяется. Вы можете запросить статус загрузки, чтобы получить более подробный список отсутствующих диапазонов. Очевидно, проблема заключалась в диапазоне и длине контента. Вы изменили конфигурацию заголовка с HttpClient на HttpRequestMessage, и теперь он отлично работал.

Комментарии:

1. Да, спасибо. Приношу извинения за задержку, это была напряженная неделя, поэтому я не смог обновить операцию до сих пор. Еще раз спасибо за всю помощь.

2. Понял @DiegoDelCastillo. Спасибо!!