草庐IT

C# - 以字节 block 的形式从 Google Drive 下载

coder 2024-05-25 原文

我目前正在为网络连接不佳的环境进行开发。我的应用程序有助于自动为用户下载所需的 Google 云端硬盘文件。它适用于小文件(从 40KB 到 2MB),但对于较大的文件(9MB)却经常失败。我知道这些文件大小可能看起来很小,但就我客户的网络环境而言,Google Drive API 经常因 9MB 文件而失败。

我已经得出结论,我需要以较小的字节 block 下载文件,但我不知道如何使用 Google Drive API 来做到这一点。我读过 this一遍又一遍,我尝试了以下代码:

// with the Drive File ID, and the appropriate export MIME type, I create the export request
var request = DriveService.Files.Export(fileId, exportMimeType);

// take the message so I can modify it by hand
var message = request.CreateRequest();
var client = request.Service.HttpClient;

// I change the Range headers of both the client, and message
client.DefaultRequestHeaders.Range =
    message.Headers.Range =
    new System.Net.Http.Headers.RangeHeaderValue(100, 200);
var response = await request.Service.HttpClient.SendAsync(message);

// if status code = 200, copy to local file
if (response.IsSuccessStatusCode)
{
    using (var fileStream = new FileStream(downloadFileName, FileMode.CreateNew, FileAccess.ReadWrite))
    {
        await response.Content.CopyToAsync(fileStream);
    }
}

然而,生成的本地文件(来自 fileStream)仍然是全长的(即 ​​40KB 驱动器文件为 40KB 文件,9MB 文件为 500 内部服务器错误)。在旁注中,我还尝试了 ExportRequest.MediaDownloader.ChunkSize,但据我观察,它只会改变 ExportRequest.MediaDownloader.ProgressChanged 回调的频率调用(即,如果 ChunkSize 设置为 256 * 1024,回调将每 256KB 触发一次)。

我该如何继续?

最佳答案

您似乎正朝着正确的方向前进。根据您上次的评论,该请求将根据 block 大小更新进度,因此您的观察是准确的。

调查 source code for MediaDownloader in the SDK发现以下(强调我的)

The core download logic. We download the media and write it to an output stream ChunkSize bytes at a time, raising the ProgressChanged event after each chunk. The chunking behavior is largely a historical artifact: a previous implementation issued multiple web requests, each for ChunkSize bytes. Now we do everything in one request, but the API and client-visible behavior are retained for compatibility.

您的示例代码只会下载 100 到 200 之间的一个 block 。使用这种方法,您必须跟踪索引并手动下载每个 block ,将它们复制到文件流中以供每次部分下载

const int KB = 0x400;
int ChunkSize = 256 * KB; // 256KB;
public async Task ExportFileAsync(string downloadFileName, string fileId, string exportMimeType) {

    var exportRequest = driveService.Files.Export(fileId, exportMimeType);
    var client = exportRequest.Service.HttpClient;

    //you would need to know the file size
    var size = await GetFileSize(fileId);

    using (var file = new FileStream(downloadFileName, FileMode.CreateNew, FileAccess.ReadWrite)) {

        file.SetLength(size);

        var chunks = (size / ChunkSize) + 1;
        for (long index = 0; index < chunks; index++) {

            var request = exportRequest.CreateRequest();

            var from = index * ChunkSize;
            var to = from + ChunkSize - 1;

            request.Headers.Range = new RangeHeaderValue(from, to);

            var response = await client.SendAsync(request);

            if (response.StatusCode == HttpStatusCode.PartialContent || response.IsSuccessStatusCode) {
                using (var stream = await response.Content.ReadAsStreamAsync()) {
                    file.Seek(from, SeekOrigin.Begin);
                    await stream.CopyToAsync(file);
                }
            }
        }
    }
}

private async Task<long> GetFileSize(string fileId) {
    var file = await driveService.Files.Get(fileId).ExecuteAsync();
    var size = file.size;
    return size;
}

此代码对驱动器 api/服务器做了一些假设。

  • 服务器将允许分块下载文件所需的多个请求。不知道请求是否受到限制。
  • 服务器仍然接受开发者文档中所述的 Range header

关于C# - 以字节 block 的形式从 Google Drive 下载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38449621/

有关C# - 以字节 block 的形式从 Google Drive 下载的更多相关文章

  1. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  2. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  3. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  4. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  5. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby-on-rails - 使用回形针的嵌套形式 - 2

    我有一个名为posts的模型,它有很多附件。附件模型使用回形针。我制作了一个用于创建附件的独立模型,效果很好,这是此处说明的View(https://github.com/thoughtbot/paperclip):@attachment,:html=>{:multipart=>true}do|form|%>posts中的嵌套表单如下所示:prohibitedthispostfrombeingsaved:@attachment,:html=>{:multipart=>true}do|at_form|%>附件记录已创建,但它是空的。文件未上传。同时,帖子已成功创建...有什么想法吗?

  8. ruby - 在匿名 block 中产生 - 2

    我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

  9. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  10. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

随机推荐