2010-02-19 5 views
1

Si je veux télécharger partiellement un fichier et définir une seule plage dans l'en-tête de demande, j'obtiens le flux d'octets du fichier demandé dans le corps de la réponse. Mais si je spécifie plusieurs plages comme ci-dessous, je reçois toujours pour chaque plage définie un en-tête de réponse supplémentaire (qui décrit la plage demandée) dans le corps de la réponse qui corrompt le fichier téléchargé.Requête HTTP avec plusieurs gammes

static void Main(string[] args) 
{ 

    Console.Write("Please enter target File: "); 
    string Target = Console.ReadLine(); 
    string Source = @"http://mozilla-mirror.3347.voxcdn.com/pub/mozilla.org/firefox/releases/3.6/win32/de/Firefox%20Setup%203.6.exe"; 

    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Source); 
    request.Credentials = CredentialCache.DefaultCredentials; 

    // define multiple Ranges 
    request.AddRange(  0, 1000000); 
    request.AddRange(1000000, 2000000); 

    HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 

    using (Stream source = response.GetResponseStream()) 
    { 
     using (FileStream target = File.Open(Target, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) 
     { 
      byte[] buffer = new byte[4096]; 
      int BytesRead = 0; 
      int TotalBytesRead = 0; 

      while((BytesRead = source.Read(buffer, 0, buffer.Length)) > 0) 
      { 
       target.Write(buffer, 0, BytesRead); 
       TotalBytesRead += BytesRead; 

       Console.WriteLine("{0}", TotalBytesRead); 
      } 
     } 
    } 

    Console.WriteLine("Downloading Finished!"); 
    Console.ReadLine(); 
} 

Demande comme indiqué dans Wireshark:

http://img197.imageshack.us/img197/8199/requesty.png 

Corps de la réponse ne doit contenir que octet-stream du fichier, mais contient en outre la réponse en-tête indésirables au début de chaque plage définie:

Response hex dump

Est-il possible d'éviter l'en-tête de réponse supplémentaire dans le corps sans demander de chaque plage séparément?

ou

Est-il possible de construire dans l'en-tête pour filtrer de réponse supplémentaire, dont la taille peut varier en fonction du serveur HTTP?

+0

Juste une question: pourquoi faites-vous cela? Je suppose que votre code ci-dessus est juste un exemple, car il est inutile de demander directement des plages contiguës comme ça? –

Répondre

1

merci pour votre aide, comme décrit dans le lien ci-dessus c'est le supposé http répond à une demande avec plusieurs plages.

so ....

Est-il possible d'éviter l'en-tête de réponse supplémentaire dans le corps sans demander séparément chaque plage?

=> Non

Y at-il un moyen de filtrer l'en-tête de réponse supplémentaire dans la construction, dont la taille pourrait varier en fonction du serveur HTTP?

=> Je ne sais pas, mais ...

peut-être certains d'entre vous pourraient avoir un regard critique sur le morceau de code suivant Wich filtre les en-têtes à partir des données du fichier:

public void DoDownload(Range[] Ranges) 
    { 

     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(m_Source); 
     request.Credentials = CredentialCache.DefaultCredentials; 

     foreach (Range r in Ranges) 
     { 
      request.AddRange(r.From, r.To); 
     } 

     HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 

     string boundary = ""; 
     Match m = Regex.Match(response.ContentType, @"^.*boundary=(?<boundary>.*)$"); 

     if (m.Success) 
     { 
      boundary = m.Groups["boundary"].Value; 
     } 
     else 
     { 
      throw new InvalidDataException("invalid packet data: no boundary specification found."); 
     } 


     using (Stream source = response.GetResponseStream()) 
     { 
      using (FileStream target = File.Open(m_TargetFile, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)) 
      { 
       // buffer for payload 
       byte[] buffer = new byte[4096]; 
       // buffer for current range header 
       byte[] header = new byte[200]; 
       // next header after x bytes 
       int NextHeader = 0; 
       // current position in header[] 
       int HeaderPosition = 0; 
       // current position in buffer[] 
       int BufferPosition = 0; 
       // left data to proceed 
       int BytesToProceed = 0; 
       // total data written without range-headers 
       long TotalBytesWritten = 0; 
       // count of last data written to target file 
       int BytesWritten = 0; 
       // size of processed header data 
       int HeaderSize = 0; 
       // count of last data read from ResponseStream 
       int BytesRead = 0; 


       while ((BytesRead = source.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        BufferPosition = 0; 
        BytesToProceed = BytesRead; 
        HeaderSize = 0; 

        while (BytesToProceed > 0) 
        { 
         if (NextHeader == 0) 
         { 
          for (;HeaderPosition < header.Length; HeaderPosition++, BufferPosition++, HeaderSize++) 
          { 
           if (BytesToProceed > HeaderPosition && BufferPosition < BytesRead) 
           { 
            header[HeaderPosition] = buffer[BufferPosition]; 

            if (HeaderPosition >= 4 && 
             header[HeaderPosition - 3] == 0x0d && 
             header[HeaderPosition - 2] == 0x0a && 
             header[HeaderPosition - 1] == 0x0d && 
             header[HeaderPosition] == 0x0a) 
            { 
             string RangeHeader = Encoding.ASCII.GetString(header, 0, HeaderPosition + 1); 
             Match mm = Regex.Match(RangeHeader, 
              @"^\r\n(--)?" + boundary + @".*?(?<from>\d+)\s*-\s*(?<to>\d+)/.*\r\n\r\n", RegexOptions.Singleline); 

             if (mm.Success) 
             { 
              int RangeStart = Convert.ToInt32(mm.Groups["from"].Value); 
              int RangeEnd = Convert.ToInt32(mm.Groups["to"].Value); 

              NextHeader = (RangeEnd - RangeStart) + 1; 

              target.Seek(RangeStart, SeekOrigin.Begin); 
              BufferPosition++; 

              BytesToProceed -= HeaderSize + 1; 

              HeaderPosition = 0; 
              HeaderSize = 0; 

              break; 
             } 
             else { throw new InvalidDataException("invalid header: missing range specification.");} 
            } 
           } 
           else { goto READ_NEW; } 
          } 

          if (NextHeader == 0) 
           throw new InvalidDataException("invalid packet data: no range-header found."); 
         } 

         BytesWritten = (NextHeader > BytesToProceed) ? BytesToProceed : NextHeader; 

         target.Write(buffer, BufferPosition, BytesWritten); 

         BytesToProceed -= BytesWritten; 
         NextHeader -= BytesWritten; 
         BufferPosition += BytesWritten; 

         TotalBytesWritten += BytesWritten; 
        } 

       READ_NEW:; 
       } 
      } 
     } 
    } 

et pourrait me donner quelques indices s'il y a une autre/meilleure façon de le faire.

meilleures salutations cap_Chap

+0

Ça fait peur. Bien qu'il ne semble pas qu'il y ait des bibliothèques de clients HTTP pour C# qui supportent les réponses en plusieurs parties (probablement parce qu'elles sont rarement utilisées). Ou peut-être que je n'ai pas l'air très dur. Quoi qu'il en soit, si vous êtes bloqué pour une alternative, pourquoi ne pas simplement faire plusieurs requêtes HTTP avec une seule gamme? –

+0

Way plus tard, mais devrait toujours être une bonne référence. La bibliothèque client WebAPI prend en charge les demandes multi-parties http: //www.c-sharpcorner.com/article/upload-et-save-multipartform-data-in-webapi-2 / –