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);
}
我在使用 .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);
}