Domanda

Ho bisogno di scaricare un di grandi dimensioni file (2 GB) su HTTP in C # applicazione console. Il problema è che, dopo circa 1,2 GB, l'applicazione esaurisce la memoria.

Ecco il codice che sto usando:

WebClient request = new WebClient();
request.Credentials = new NetworkCredential(username, password);
byte[] fileData = request.DownloadData(baseURL + fName);

Come si può vedere ... sto leggendo il file direttamente nella memoria. Sono abbastanza sicuro che avrei potuto risolvere questo se dovessi leggere i dati di ritorno da HTTP in blocchi e scrivere in un file sul disco.

Come potrei fare questo?

È stato utile?

Soluzione

Se si utilizza WebClient. DownloadFile potrebbe risparmiare direttamente in un file.

Altri suggerimenti

La classe WebClient è quello per scenari semplificati. Una volta passato scenari semplici (e si dispone), si dovrà ripiegare un po 'e utilizzare WebRequest.

Con WebRequest, avrete accesso al flusso di risposta, e sarete in grado di ciclo su di esso, a leggere un po 'e scrivere un po', fino a quando il gioco è fatto.


Esempio:

public void MyDownloadFile(Uri url, string outputFilePath)
{
    const int BUFFER_SIZE = 16 * 1024;
    using (var outputFileStream = File.Create(outputFilePath, BUFFER_SIZE))
    {
        var req = WebRequest.Create(url);
        using (var response = req.GetResponse())
        {
            using (var responseStream = response.GetResponseStream())
            {
                var buffer = new byte[BUFFER_SIZE];
                int bytesRead;
                do
                {
                    bytesRead = responseStream.Read(buffer, 0, BUFFER_SIZE);
                    outputFileStream.Write(buffer, 0, bytesRead);
                } while (bytesRead > 0);
            }
        }
    }
}

Si noti che se WebClient.DownloadFile funziona, allora lo chiamerei la soluzione migliore. Ho scritto quanto sopra prima della risposta "DownloadFile" è stata pubblicata. Ho anche scritto troppo presto la mattina, quindi può essere richiesto un grano di sale (e test).

È necessario per ottenere il flusso di risposta e quindi leggere in blocchi, scrivendo ogni blocco in un file per consentire la memoria per essere riutilizzato.

Come hai scritto, tutta la risposta, tutto 2GB, ha bisogno di essere in memoria. Anche su un sistema a 64 bit che ha colpito il limite di 2 GB per un singolo oggetto .NET.


Aggiornamento: l'opzione più facile. Ottenere WebClient per fare il lavoro per voi: con la sua DownloadFile metodo che metterà i dati direttamente in un file.

WebClient.OpenRead restituisce un flusso, basta usare Read per ciclo sul contenuto, quindi i dati non vengono accodati in memoria, ma possono essere scritti in blocchi in un file.

Vorrei usare qualcosa di simile a questo

Il collegamento può essere interrotto, quindi è meglio per scaricare il file in piccoli pezzi.

flussi di Akka possono aiutare il download di file in piccoli pezzi da uno System.IO.Stream con multithreading. https://getakka.net/articles/intro/what-is-akka. html

Il metodo di download aggiungerà i byte al file che inizia con una lunga fileStart. Se il file non esiste, il valore fileStart deve essere 0.

using Akka.Actor;
using Akka.IO;
using Akka.Streams;
using Akka.Streams.Dsl;
using Akka.Streams.IO;

private static Sink<ByteString, Task<IOResult>> FileSink(string filename)
{
    return Flow.Create<ByteString>()
        .ToMaterialized(FileIO.ToFile(new FileInfo(filename), FileMode.Append), Keep.Right);
}

private async Task Download(string path, Uri uri, long fileStart)
{
    using (var system = ActorSystem.Create("system"))
    using (var materializer = system.Materializer())
    {
       HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
       request.AddRange(fileStart);

       using (WebResponse response = request.GetResponse())
       {
           Stream stream = response.GetResponseStream();

           await StreamConverters.FromInputStream(() => stream, chunkSize: 1024)
               .RunWith(FileSink(path), materializer);
       }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top