2010-04-26 3 views
6

Je voudrais utiliser le WebClient (ou il y a une autre meilleure option?) Mais il y a un problème. Je comprends que l'ouverture du flux prend du temps et cela ne peut être évité. Cependant, lire cela prend étrangement beaucoup plus de temps que de le lire entièrement immédiatement.Comment télécharger un fichier en chaîne avec rappel de progression?

Existe-t-il une meilleure façon de procéder? Je veux dire deux façons, de ficeler et de classer. Progress est mon propre délégué et ça fonctionne bien.


Cinquième rapport:

Enfin, je réussi à le faire. En attendant j'ai vérifié quelques solutions qui m'ont fait réaliser que le problème est ailleurs.

J'ai testé des objets personnalisés WebResponse et WebRequest, la bibliothèque libCURL.NET et même Sockets.

La différence de temps était la compression gzip. La longueur du flux compressé était simplement la moitié de la longueur du flux normal et donc le temps de téléchargement était inférieur à 3 secondes avec le navigateur.

Je mets un peu de code si quelqu'un voudra savoir comment je résolu ce problème: (certains en-têtes ne sont pas nécessaires)

public static string DownloadString(string URL) 
    { 
     WebClient client = new WebClient(); 
     client.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1045 Safari/532.5"; 
     client.Headers["Accept"] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; 
     client.Headers["Accept-Encoding"] = "gzip,deflate,sdch"; 
     client.Headers["Accept-Charset"] = "ISO-8859-2,utf-8;q=0.7,*;q=0.3"; 

     Stream inputStream = client.OpenRead(new Uri(URL)); 
     MemoryStream memoryStream = new MemoryStream(); 
     const int size = 32 * 4096; 
     byte[] buffer = new byte[size]; 

     if (client.ResponseHeaders["Content-Encoding"] == "gzip") 
     { 
      inputStream = new GZipStream(inputStream, CompressionMode.Decompress); 
     } 

     int count = 0; 
     do 
     { 
      count = inputStream.Read(buffer, 0, size); 
      if (count > 0) 
      { 
       memoryStream.Write(buffer, 0, count); 
      } 
     } 
     while (count > 0); 

     string result = Encoding.Default.GetString(memoryStream.ToArray()); 
     memoryStream.Close(); 
     inputStream.Close(); 
     return result; 
    } 

Je pense que les fonctions asyncro seront presque les mêmes. Mais je vais simplement utiliser un autre thread pour déclencher cette fonction. Je n'ai pas besoin d'indication de progression.

+0

Qu'est-ce qui n'est pas si bon? Où est la chaîne à laquelle vous écrivez? – Marcel

+0

Ce n'est pas un bon résultat parce que seulement 2048 (iSize) caractères de la fin du fichier. Et c'est très lent à télécharger tout. – Kaminari

+0

Vous semblez lire le même 'Stream 'à deux endroits; 'lecteur' et' streamRemote' ... Je suis confus ... Aussi; attention: lorsque vous manipulez des octets, vous ne lisez pas toujours les blocs ('iByteSize') qui sont des nombres entiers de caractères. Il n'est également pas très clair comment les mises à jour de l'interface utilisateur se produisent dans le rappel; si cela implique un thread-switch qui ajoutera des retards (tout comme le 'DoEvents 'maudit). –

Répondre

1

Vous obtenez uniquement les derniers iSize octets de votre fichier puisque vous remplacez votre tampon à chaque itération, vous n'enregistrez le tampon nulle part. Voici un exemple sur la façon de stocker le fichier en mémoire à l'aide d'un MemoryStream.

var totalBytes = new MemoryStream(1024 * 1024); 
while ((iByteSize = streamRemote.Read(byteBuffer, 0, iByteSize)) > 0) 
{ 
    totalBytes.Write(byteBuffer, 0, iByteSize); 
    iRunningByteTotal += iByteSize; 

    //Some progress calculation 
    if (Progress != null) Progress(iProgressPercentage); 
} 

Une fois le téléchargement terminé, vous pouvez le convertir en texte.

var byteArray = totalBytes.GetBuffer(); 
var numberOfBytes = totalBytes.Length; 
var text = Encoding.Default.GetString(byteArray, 0, numberOfBytes); 

Mise à jour: la méthode DownloadStringAsync fait essentiellement la même chose que ci-dessus, mais ne vous donnera aucune indication de progrès. Il existe cependant d'autres méthodes asynchrones qui déclenchent l'événement DownloadProgressChanged.

Mise à jour 2: Concernant le temps de réponse. Avez-vous chronométré le téléchargement de la ressource en utilisant un autre outil? Les principaux navigateurs ont intégré le support pour le chronométrage de tels changements.

En outre, est-ce un fichier statique que vous diffusez ou le contenu généré sur le serveur?

Une troisième chose qui me vient à l'esprit est la mise en mémoire tampon sur le serveur. Par exemple. Si la propriété Response.Buffer dans ASP.Net est utilisée, rien ne sera envoyé au client tant que le fichier/la page n'aura pas été entièrement exécuté sur le serveur. Ainsi, le client devra attendre avant de pouvoir commencer le téléchargement.

+0

Le navigateur charge la page entière en environ 5 secondes. Télécharger La vitesse de la chaîne asynchrone est la même. Mais étrangement je reçois plusieurs 0 progrès et 100 à la fin. Ce n'est pas aussi asynchrone que je le pense. – Kaminari

+0

@Kaminari - de quelle taille parle-t-on ici? S'ils sont petits, il serait probablement plus efficace de vous servir tous en même temps, plutôt que de couper le téléchargement en petits morceaux. –

+0

@Peter Lillevold ce n'est pas un fichier. C'est un site web. Mais seulement html sans autres éléments. Taille environ 1,33MB. – Kaminari

1

Je suis très confus par la double lecture, mais il semble comme vous réellement l'intention de faire quelque chose comme:

 StringBuilder sb = new StringBuilder();   
     using (StreamReader reader = new StreamReader(streamRemote)) 
     { 
      char[] charBuffer = new char[bufferSize]; 
      int charsRead; 
      while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0) 
      { 
       sb.Append(charBuffer, 0, charsRead); 
       //Some progress calculation 

       if (Progress != null) Progress(iProgressPercentage); 
      } 
     } 
     string result = sb.ToString(); 

Voir si cela fonctionne comme vous le souhaitez .. Je me demande, cependant, si le Progress n'est pas la cause de la chute; Essayez-le sans cela assigné, voir si cela le rend plus rapide. Ou ne l'exécutez que périodiquement:

  //[snip] 
      int iteration = 0, charsRead; 
      while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0) 
      { 
       sb.Append(charBuffer, 0, charsRead); 
       //Some progress calculation 
       if((++iteration % 20) == 0 && Progress != null) { 
        Progress(iProgressPercentage); 
       } 
      } 
      //[snip] 

Également, essayez d'augmenter la taille de la mémoire tampon.

+0

Mais pourquoi l'ouverture du flux prend autant de temps que le navigateur Web télécharge la page entière? – Kaminari

Questions connexes