【问题标题】:Google Drive API V3 Out Of memory downloading large filesGoogle Drive API V3 内存不足下载大文件
【发布时间】:2021-01-19 03:02:24
【问题描述】:

我在使用 .NET V3 nuGet 包从 Google Drive 下载超过 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 来设置它。我看不到任何设置范围标题的方法。我尝试过使用块大小,但无济于事。

【问题讨论】:

  • 这是一个有趣的问题,我以前没有遇到过。你可能想把它交叉发布到图书馆github.com/googleapis/google-api-dotnet-client/issues
  • 这有帮助吗? stackoverflow.com/questions/38449621/… // 在 developers.google.com/api-client-library/dotnet/guide/… 中也有对 progress.BytesDownloaded 的引用 - 也许这也有帮助?
  • 我正在循环上面代码中的 BytesDownloaded。那就是输出。我似乎无法访问 Range 标头。我找不到任何使用 Google API 中的 MediaDownloader 功能分块下载的具体示例。
  • 在我链接的堆栈问题中,引用了var request = exportRequest.CreateRequest(); request.Headers.Range = new RangeHeaderValue(from, to); - 这不起作用吗?
  • 我停止使用内存流,而是使用文件流,之后代码就可以工作了。我能够显式转换为 MediaDownloader 以获取 Range 值,但在下载 1.2 - 1.3 GB 后它仍然内存不足。所以,我所做的就是将 MemoryStream 切换到 FileStream。我将流写入磁盘而不是缓冲。它给我留下了一个问题,我现在可以在挂起之前直接下载 5 或 6 个文件(每个文件大约 3-4 GB)。这可以通过在每次下载后刷新和关闭 FileStream 来解决。我会正式回答这个问题。

标签: c# google-api google-drive-api


【解决方案1】:

因此,在与 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);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 2023-02-06
    • 1970-01-01
    相关资源
    最近更新 更多