2016-07-19 166 views
13

Attualmente sto sviluppando per un ambiente con scarsa connettività di rete. La mia applicazione aiuta a scaricare automaticamente i file di Google Drive richiesti per gli utenti. Funziona abbastanza bene per file di piccole dimensioni (da 40KB a 2MB), ma fallisce troppo spesso per file più grandi (9MB). So che queste dimensioni dei file potrebbero sembrare ridotte, ma in termini di ambiente di rete del mio cliente, l'API di Google Drive fallisce costantemente con il file da 9 MB.C# - Download da Google Drive in blocchi di byte

Ho concluso che ho bisogno di scaricare file in blocchi di byte più piccoli, ma non vedo come posso farlo con l'API di Google Drive. Ho letto this più e più volte, e ho provato il seguente codice:

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

Il file locale risultante (da fileStream), tuttavia, è ancora piena di lunghezza (cioè file di 40 KB per il file di 40 KB unità e un errore interno di 500 server per il file da 9 MB). In un sidenote, ho anche sperimentato con ExportRequest.MediaDownloader.ChunkSize, ma da quello che osservo cambia solo la frequenza con cui viene richiamata la richiamata ExportRequest.MediaDownloader.ProgressChanged (vale a dire la richiamata attiverà ogni 256 KB se ChunkSize è impostata su 256 * 1024).

Come posso procedere?

+0

Hai provato ispezionare la richiesta/risposta utilizzando un proxy come http://www.telerik.com/fiddler? Farei doppio controllo sul fatto che il mio cliente sta inviando l'intestazione del range nel modo in cui lo desidero. Inoltre, il post del blog msdn - https://blogs.msdn.microsoft.com/webdev/2012/11/23/asp-net-web-api-and-http-byte-range-support/ - sembra essere un poco utile (anche se focalizzato sul server), e indica che è necessario aspettarsi un codice di ritorno 206, non un 200 se a) si invia la richiesta corretta eb) il server supporta l'intestazione intervallo.Detto questo, sembra che tu stia andando nella giusta direzione. – Sean

+0

@matt, hai mai risolto questo? – Nkosi

+0

@Nkosi Ci scusiamo per la risposta solo ora, alla fine ho rinunciato al progetto, quindi non l'ho mai risolto. Se tu (o chiunque altro) trovi una soluzione, ti preghiamo di postarla come risposta! – matt

risposta

5

Sembravi andare nella giusta direzione. Dal tuo ultimo commento, la richiesta aggiornerà il progresso in base alla dimensione del blocco, quindi l'osservazione è stata accurata.

Guardando al source code for MediaDownloader in the SDK è stata trovata (sottolineatura mia) il seguente

La logica scaricare nucleo. Scarichiamo il supporto e lo scriviamo su un flusso di output , ChunkSize byte alla volta, , che genera l'evento ProgressChanged dopo ogni blocco. Il comportamento di chunking è in gran parte un artefatto storico : una precedente implementazione ha generato più richieste Web, ciascuna per byte ChunkSize. Ora facciamo tutto in una richiesta, ma l'API e il comportamento client-client vengono mantenuti per compatibilità.

Il tuo esempio di codice verrà scaricato solo un pezzo da 100 a 200. Utilizzando questo approccio si dovrebbe tenere traccia di un indice e scaricare ogni pezzo manualmente, copiarli nel flusso di file per ogni download parziale

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

Questo codice formula alcune ipotesi sull'unità disco/server.

  • Che il server consentirà più richieste necessarie per scaricare il file in blocchi. Non so se le richieste sono limitate.
  • che il server accetta ancora l'intestazione Range come indicato nel documenation sviluppatore