Google 驱动器 API V3 下载大文件时内存不足

Google Drive API V3 Out Of memory downloading large files

我在使用 .NET V3 nuGet 包从 Google 驱动器下载超过 1.5 GB 的大文件时遇到问题。

使用下面的代码:

public void DownloadFile(string fileId, string saveTo)
    {
        var request = service.Files.Get(fileId);
        var stream = new System.IO.MemoryStream();
        // Add a handler which will be notified on progress changes.
        // It will notify on each chunk download and when the
        // download is completed or failed.
        request.MediaDownloader.ProgressChanged += (Google.Apis.Download.IDownloadProgress progress) =>
        {
            switch (progress.Status)
            {
                case Google.Apis.Download.DownloadStatus.Downloading:
                    {
                        Console.WriteLine(progress.BytesDownloaded);
                        break;
                    }
                case Google.Apis.Download.DownloadStatus.Completed:
                    {
                        Console.WriteLine("Download complete.");
                        SaveStream(stream, saveTo);
                        break;
                    }
                case Google.Apis.Download.DownloadStatus.Failed:
                    {
                        Console.WriteLine("Download failed.");
                        break;
                    }
            }
        };

        request.Download(stream);
        GC.Collect();
    }
    public void SaveStream(System.IO.MemoryStream stream, string saveTo)
    {
        using (System.IO.FileStream file = new System.IO.FileStream(saveTo, System.IO.FileMode.Create, System.IO.FileAccess.Write))
        {
            try
            {
                stream.WriteTo(file);
            }
            catch(Exception ex)
            {
            }
        }
    }

我在 Google.Apis.Download.IDownloadProgress 中遇到异常:

抛出了 'System.OutOfMemoryException' 类型的异常。

堆栈跟踪是:

   at System.IO.MemoryStream.set_Capacity(Int32 value)
   at System.IO.MemoryStream.EnsureCapacity(Int32 value)
   at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.IO.MemoryStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Google.Apis.Download.MediaDownloader.<DownloadCoreAsync>d__31.MoveNext()

控制台输出如下所示:

.
.
.

1310720000
1321205760
1331691520
1342177280
Download failed.

就下载的字节数而言,它通常会在同一点左右失败。

有什么办法既可以使用部分下载,也可以恢复下载?我找不到太多文档或示例。我似乎无法访问 request.MediaDownloader.Range 来设置它。我看不到任何设置范围 header 的方法。我试过使用 chunksizes 无济于事。

因此,在咨询了来自 GitHub 线程 here 的人后,我能够使用 FileStream 而不是 MemoryStream,并且我成功下载了 4GB 的文件。

这导致了另一个问题,我可以直接下载 5 或 6 个文件,然后程序会挂在第 7 个文件上。我找到的解决方法是在每次下载后对 FileStream 执行 .Flush() 和 .Close()。在我这样做之后,我能够直接下载 20 多个文件,每个文件大小为 3/4 GB(这些是 .ISO 图像文件)。

public void DownloadFile(string fileId, string saveTo)
    {
        var request = service.Files.Get(fileId);
        FileStream fileStream = new FileStream(saveTo, FileMode.OpenOrCreate, FileAccess.Write);
        // Add a handler which will be notified on progress changes.
        // It will notify on each chunk download and when the
        // download is completed or failed.   
        request.MediaDownloader.ProgressChanged += (Google.Apis.Download.IDownloadProgress progress) =>
        {
            switch (progress.Status)
            {
                case Google.Apis.Download.DownloadStatus.Downloading:
                    {
                        Console.WriteLine(progress.BytesDownloaded);
                        break;
                    }
                case Google.Apis.Download.DownloadStatus.Completed:
                    {
                        Console.WriteLine("Download complete.");
                        fileStream.Flush();
                        fileStream.Close();
                        break;
                    }
                case Google.Apis.Download.DownloadStatus.Failed:
                    {
                        Console.WriteLine("Download failed.");
                        break;
                    }
            }
        };
        request.Download(fileStream);
    }