使用 Google Drive .NET API 上传文件时,有时会出现 Null ResponseBody

Null ResponseBody sometimes, when uploading file using Google Drive .NET API

当我将一些文件发送到 google 驱动器时,我有时会得到 null ResponseBody。

这是随机发生的,大部分文件都已发送,但 10 个文件中有 1 个 return null ResponseBody。

我搜索了这个,但只有关于 aways 的问题得到了 null here, and here

我的代码:

await UploadFileAsync(...); //exception here

public Task<string> UploadFileAsync(DriveService driveService, string LocalPath, string gdriveFileName, string GDriveFolder, IProgress<long> progress)
{
            return Task.Run(() =>
            {
                var folderId = GetDirectoryOrCreateIfNotExist(driveService, GDriveFolder);
                var file = CreateGDriveFile(gdriveFileName, folderId);

                IUploadProgress result = null;
                FilesResource.CreateMediaUpload request;
                using (var stream = new System.IO.FileStream(LocalPath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    request = driveService.Files.Create(file, stream, "image/jpeg");
                    request.ChunkSize = ResumableUpload.MinimumChunkSize * 4;

                    request.ProgressChanged += (p) => progress.Report(p.BytesSent * 100 / stream.Length);

                    request.Fields = "id, webContentLink, name";
                    result = request.Upload();
                }

                if (request == null)
                    throw new Exception("O request é nulo");

                if (request.ResponseBody == null)
                    throw new Exception("O ResponseBody é nulo"); //Throw here sometimes

                var fileUploaded = request.ResponseBody;

                return fileUploaded.WebContentLink;
            });
}

private string GetDirectoryOrCreateIfNotExist(DriveService driveService, string folderName)
        {
            var folder = GetFolderIdByName(driveService, folderName).FirstOrDefault();

            if (folder == null)
            {
                folder = CreateGDriveDirectory(driveService, folderName);
                AplyPermissionToFile(driveService, folder);
            }

            return folder;
        }

private File CreateGDriveFile(string fileName, string folderName = null)
        {
            var file = new File()
            {
                Name = fileName
            };

            if (folderName != null)
            {
                file.Parents = new List<string>
                {
                    folderName
                };
            }

            return file;
        }

为什么 ResponseBody 为空?为什么这种情况只会发生几次?

更新:

我编辑 UploadFileAsync(),检查 UploadStatus,现在我得到:

The service drive has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Internal Error [500]
Errors [
    Message[Internal Error] Location[ - ] Reason[internalError] Domain[global]
]

更新代码:(参见 result.Status != UploadStatus.Completed)

public Task<string> UploadFileAsync(DriveService driveService, string LocalPath, string gdriveFileName, string GDriveFolder, IProgress<long> progress)
        {
            return Task.Run(() =>
            {
                var folderId = GetDirectoryOrCreateIfNotExist(driveService, GDriveFolder);
                var file = CreateGDriveFile(gdriveFileName, folderId);

                IUploadProgress result = null;
                FilesResource.CreateMediaUpload request;
                using (var stream = new System.IO.FileStream(LocalPath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    request = driveService.Files.Create(file, stream, "image/jpeg");
                    request.ChunkSize = ResumableUpload.MinimumChunkSize * 4;

                    request.ProgressChanged += (p) => progress.Report(p.BytesSent * 100 / stream.Length);

                    request.Fields = "id, webContentLink, name";
                    result = request.Upload();
                }

                if (result.Status != UploadStatus.Completed) //check status, now it's throwing here sometimes
                    throw result.Exception;

                if (request == null)
                    throw new Exception("O request é nulo");

                if (request.ResponseBody == null)
                    throw new Exception("O ResponseBody é nulo");

                var fileUploaded = request.ResponseBody;

                return fileUploaded.WebContentLink;
            });
        }

有人可以帮助我吗?

你明白了 500 Error Code because this is an issue related to exponential backoff。由于您尝试发出的并发请求的数量,它会发生。

因此,在您的代码中,您需要应用一些逻辑来避免该问题。例如这个 algorithm:

If the request fails, wait 1 + random_number_milliseconds seconds and retry the request.

If the request fails, wait 2 + random_number_milliseconds seconds and retry the request.

If the request fails, wait 4 + random_number_milliseconds seconds and retry the request.

And so on, up to a maximum_backoff time.

where:

The wait time is min(((2^n)+random_number_milliseconds), maximum_backoff), with n incremented by 1 for each iteration (request).

random_number_milliseconds is a random number of milliseconds less than or equal to 1000. This helps to avoid cases where many clients get synchronized by some situation and all retry at once, sending requests in synchronized waves. The value of random_number_milliseconds is recalculated after each retry request.

maximum_backoff is typically 32 or 64 seconds. The appropriate value depends on the use case.

根据 alberto vielma 的回答,我的代码现在可以工作了,看起来像这样:

    public Task<string> UploadFileAsync(DriveService driveService, string LocalPath, string gdriveFileName, string GDriveFolder, IProgress<long> progress)
    {
            return Task.Run(() =>
            {
                var folderId = GetDirectoryOrCreateIfNotExist(driveService, GDriveFolder);
                var file = CreateGDriveFile(gdriveFileName, folderId);

                IUploadProgress result = null;
                FilesResource.CreateMediaUpload request;
                using (var stream = new System.IO.FileStream(LocalPath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    request = driveService.Files.Create(file, stream, "image/jpeg");
                    request.ChunkSize = ResumableUpload.MinimumChunkSize * 4;

                    request.ProgressChanged += (p) => progress.Report(p.BytesSent * 100 / stream.Length);

                    request.Fields = "id, webContentLink, name";
                    result = request.Upload();

                    //solution: Truncated exponential backoff
                    if (result.Status != UploadStatus.Completed)
                    {
                        var rdn = new Random();
                        var waitTime = 0;
                        var count = 0;
                        do
                        {
                            waitTime = (Convert.ToInt32(Math.Pow(2, count)) * 1000) + rdn.Next(0, 1000);
                            Thread.Sleep(waitTime);

                            result = request.Upload();
                            count++;

                        } while (count < 5 && (result.Status != UploadStatus.Completed));
                    }//end solution
                }

                if (result.Status != UploadStatus.Completed)
                    throw result.Exception; //Doesn't get here anymore

                var fileUploaded = request.ResponseBody;

                return fileUploaded.WebContentLink;
            });
    }