2016-07-19 10 views
13

Ich entwickle derzeit für eine Umgebung, die schlechte Netzwerkkonnektivität hat. Meine Anwendung hilft beim automatischen Herunterladen erforderlicher Google Drive-Dateien für Benutzer. Es funktioniert ziemlich gut für kleine Dateien (von 40 KB bis 2 MB), aber scheitert viel zu oft für größere Dateien (9 MB). Ich weiß, dass diese Dateigrößen klein erscheinen mögen, aber in Bezug auf die Netzwerkumgebung meines Kunden schlägt die Google Drive API ständig mit der 9 MB-Datei fehl.C# - Herunterladen von Google Drive in Byte-Chunks

Ich habe festgestellt, dass ich Dateien in kleineren Byte-Chunks herunterladen muss, aber ich sehe nicht, wie ich das mit Google Drive API tun kann. Ich habe this über gelesen und immer wieder, und ich habe den folgenden Code versucht:

// 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); 
    } 
} 

Die resultierende lokale Datei (von fileStream) ist jedoch nach wie vor in voller Länge (dh 40 KB-Datei für die 40KB Drive-Datei und ein 500 Interner Serverfehler für die 9 MB-Datei). Als Nebenbemerkung habe ich auch mit ExportRequest.MediaDownloader.ChunkSize experimentiert, aber von dem, was ich beobachte, ändert es nur die Frequenz, bei der der Rückruf aufgerufen wird (d. H. Callback löst alle 256 KB aus, wenn ChunkSize auf 256 * 1024 gesetzt ist).

Wie kann ich fortfahren?

+0

Sie die Anfrage/Antwort-Inspektion eines Proxy wie http://www.telerik.com/fiddler versucht haben, mit? Ich würde überprüfen, ob mein Client den Bereichskopf so sendet, wie ich es möchte. Darüber hinaus scheint der msdn-Blog-Post - https://blogs.msdn.microsoft.com/webdev/2012/11/23/asp-net-web-api-and-http-byte-range-support/ - ein a wenig hilfreich (auch wenn es serverfokussiert ist) und angibt, dass Sie einen Rückgabecode von 206 erwarten sollten, nicht 200, wenn a) Sie die richtige Anfrage senden und b) der Server den Bereichskopf unterstützt.Das heißt, es sieht so aus, als ob Sie in die richtige Richtung gehen. – Sean

+0

@matt, hast du das jemals gelöst? – Nkosi

+0

@Nkosi Sorry für die Antwort nur jetzt, gab ich schließlich auf das Projekt, also nein, ich habe es nie gelöst. Wenn Sie (oder irgendjemand) jemals eine Lösung finden, posten Sie es bitte als Antwort! – matt

Antwort

5

Sie schienen in die richtige Richtung zu gehen. Ab dem letzten Kommentar aktualisiert die Anforderung den Fortschritt basierend auf der Chunk-Größe, sodass Ihre Beobachtung korrekt war.

Blick in die source code for MediaDownloader in the SDK folgenden (Hervorhebung von mir)

Der Kern Logik Download gefunden wurde. Wir laden die Medien und schreiben sie in einen Ausgabestrom ChunkSize Bytes zu einer Zeit, Anhebung der ProgressChanged Ereignis nach jedem Stück. Das Chunking-Verhalten ist weitgehend ein historisches -Artefakt: Eine vorherige Implementierung gab mehrere Webanforderungen aus, jede für ChunkSize-Bytes. Jetzt machen wir alles in eine Anfrage, aber die API und Client-sichtbare Verhalten werden für die Kompatibilität beibehalten.

Ihr Codebeispiel wird nur ein Klumpen von 100 bis 200 herunterladen, diesen Ansatz verwenden Sie jeden Chunk manuell verfolgen einen Index haben würden und downloaden, sie auf den Datei-Stream für jeden Teil herunterladen Kopieren

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; 
} 

Dieser Code macht einige Annahmen über das Laufwerk API/Server.

  • Der Server wird die mehreren Anforderungen ermöglichen, die zum Herunterladen der Datei in Chunks benötigt werden. Ich weiß nicht, ob Anfragen gedrosselt werden.
  • Dass der Server immer noch die Range Header akzeptiert wie in den Entwickler documenation angegeben
Verwandte Themen