Bindärdaten via HTTP downloaden und in Datei schreiben

Hallo,

ich suche mich seit einigen Tagen durch das Internet und finde immer noch nichts, was mir richtig hilft.

Mein Ziel ist es, eine Binärdatei (in dem Fall eine .exe) über HTTP herunterzuladen und in einer Datei zu speichern. Die Originaldatei ist etwa 12 MB groß.

Den nötigen Request speichere ich in einem char-Array (buffer), das eine größe von 1024 Feldern hat:
Code:
strcpy(buffer,"GET /~home/testprogramm.exe HTTP/1.0\r\nUser-agent: http-download\r\n\r\n");
Der Empfang wird dann folgendermaßen vollzogen (char buffer[1024] und (std::)string response):
Code:
bytes = recv(s,buffer,BUFFER_SIZE,0);
while(bytes > 0){
      gesamtb = gesamtb + bytes;            
      cout <<"empfangen: " << gesamtb << endl;
      response += buffer;
      if(response.size() >= BUFFER_SIZE*2){
        schreibe_daten("testprogramm.exe",response);
        response.clear();
      }
      bytes = recv(s,buffer,BUFFER_SIZE,0);
    }
Der Empfang funktioniert, ich bekomme angezeigt, dass gerundet 12 MB empfangen wurden (die Zeile cout <<"empfangen: " << gesamtb << endl; gibt das jedenfalls so aus).

Nach der Schleife wird noch einmal die Funktion schreibe_daten("testprogramm.exe", response) aufgerufen, damit auch alle Daten geschrieben werden.
Die Funktion ist bei mir so implementiert:
Code:
void schreibe_daten(string dateiname, string data){
  ifstream testedatei;
  testedatei.open(dateiname.c_str(),ios_base::in);
  if(testedatei.good()){
    testedatei.close();
    ofstream ausgabe_datei;
    ausgabe_datei.open(dateiname.c_str(),fstream::out | fstream::binary | fstream::app);
    //ausgabe_datei.write(data.data(),sizeof(data.data()));
    ausgabe_datei << data.data();
    ausgabe_datei.close();
  }
  else {
    testdatei.close();
    cout << "Datei wird angelegt... " << endl;
    ofstream anlege_datei;
    anlege_datei.open(dateiname.c_str(),fstream::out | fstream::binary);
    //anlege_datei.write(data.data(),sizeof(data.data()));
    anlege_datei << data.data();
    anlege_datei.close();
  }
}
Die auskommentierten Zeilen zeigen meinen ersten Versuch, ich dachte die Alternative mit dem <<-Operator würde vielleicht funktionieren.

Aber es werden nur etwas mehr als 3MB übertragen und ich komme nicht weiter.

Da der Empfang mir tatsächlich die ~12MB anzeigt, gehe ich davon aus, dass ich die Filestreams falsch benutze.

Findet jemand einen (Logik-)Fehler?

Danke schonmal im voraus :)

PS: der HTTP-Header wird bis dato noch mit in die Datei geschrieben, aber daran wird es wohl nicht liegen oder?
 
Bei binären Dateien hatte ich auch immer Probleme mit den Streamoperationen << und >>. Ich vermute mal, dass dort der Hund begraben liegt. Du hast folgende Zeile ja schon mal ausprobiert. Was war denn das Ergebnis?
Code:
anlege_datei.write(data.data(),sizeof(data.data()));
Zudem schadet es auch nie ein paar Flags mehr anzugeben als nötig:
Code:
anlege_datei.open(dateiname.c_str(),fstream::out | fstream::binary | fstream::ate);
mfg benediktibk
 
Danke für deinen Beitrag.

Hab das gerade nochmal probiert:
Mit
Code:
anlege_datei.write(data.data(),sizeof(data.data()));
werden sogar nur 6,33KB (laut Explorer) geschrieben.

Das fstream::ate Flag hat in dem Zusammenhang keine Veränderung bewirkt.
 
Schau mal hier. In deinem Codeschnipsel kann ich nirgends feststellen, dass du den Header entfernst.
Sonst lade die Datei doch auch mal mit Firefox runter und vergleiche die dann mit einem Hex-Editor - Aber nimm dann besser eine kleinere Datei >.> . Einige bieten diese Vergleichsfunktion direkt.
 
Danke!
Das werde ich mir mal aneignen, ich glaube in der HEAD-Variante einen Lösungsansatz gefunden zu haben.
Werde das sogleich mal testen.
 
So, ein kleines Update:
Bisher funktioniert es nicht, aber ich denke, dass ich einen sinnvollen Ansatz gefunden habe:

Zuerst wird die Größe der Datei ermittelt
Code:
strcpy(buffer,"HEAD /~home/testprogramm.exe HTTP/1.0\r\nUser-agent: http-download\r\n\r\n");
    
    int sent;
    sent = send(s,buffer,strlen(buffer),0);
    if(sent == -1) {
      cout << "Anfrage fehlgeschlagen: " << buffer << endl;
    }
    
    int byte;
    byte = recv(s,buffer,BUFFER_SIZE,0); 
    cout << buffer << endl;
    
    //hier wird die Größe ermittelt
    header.clear();
    header = buffer;
    string::size_type finder;
    finder = header.find("Content-Length:");
    header.erase(0,finder+16);
    finder = header.find("\r\n");
    header.erase(finder);
    
    int dateigroesse;
    dateigroesse = atoi(header.c_str());
    cout << dateigroesse << "B sind zu downloaden!" << endl;

Danach wird zwischen großen Dateien und kleinen Dateien unterschieden, um die Puffergröße (und damit die Größe der Teile) anzupassen, da das Testprogramm aber 12 MB groß ist (und sich erstmal nicht ändert), lasse ich die Unterscheidung raus.

Der nächste Abschnitt ist mein Downloadcode inklusive dem generieren der Requests:

Code:
      int gesamtbyte, count;
      byte = 0;
      gesamtbyte = 0;
      int teilgroesse = 10240;
      count = 0;
     
      ofstream downloadlog;
      downloadlog.open(".\\log\\downloadrequests.log",ios_base::out | ios_base::app | ios_base::ate);

      while(gesamtbyte < dateigroesse){
        strcpy(buffer,"GET /~home/testprogramm.exe HTTP/1.0\r\nUser-agent: http-download\r\nRange: bytes=");
        
        strcat(buffer,int2str(count*teilgroesse).c_str());
        strcat(buffer,"-");
        strcat(buffer,int2str((count+1)*teilgroesse-1).c_str());
        strcat(buffer,"\r\n\r\n");
        
        downloadlog << buffer << "gesamtbytes: " << gesamtbyte << endl;
        
        sent = send(s,buffer,strlen(buffer),0);
        if(sent == -1){
          cout << "Senden fehlgeschlagen!" << endl;
        }
        cout << sent << endl;
        
        byte = recv(s,rbuffer,BUFFER_SIZE+1024,0); //char rbuffer[BUFFER_SIZE+1024]; ersetzt std::string response;
        gesamtbyte += byte;
        cout << byte << " " << rbuffer << endl;
        count++;
      }

Die funktion int2str habe ich selbst geschrieben und sie liefert auch die richtigen Werte. Die Requests in der Logdatei sind auch richtig.


Das Problem ist, dass ich keine Antwort vom Server bekomme. Also nicht einmal 400 Bad Request, sondern wirklich nichts. byte ist in der while-Schleife immer 0, ich empfange keine Daten. Der Server ist aber an, die Datei liegt darauf (sonst würde ich ja 404 erwarten).

Aber ich find es kurios, dass ich keine Antwort bekomme, obwohl der Request gesendet wird. (lasse mir ja per cout anzeigen, wieviel gesendet wird).

Kann mir da jemand helfen?
 
Hmm... Was willst du denn mit der While-Schleife bezwecken?
Code:
while(gesamtbyte < dateigroesse){
        strcpy(buffer,"GET /~home/testprogramm.exe HTTP/1.0\r\nUser-agent: http-download\r\nRange: bytes=");
        
        strcat(buffer,int2str(count*teilgroesse).c_str());
        strcat(buffer,"-");
        strcat(buffer,int2str((count+1)*teilgroesse-1).c_str());
        strcat(buffer,"\r\n\r\n");
        
        downloadlog << buffer << "gesamtbytes: " << gesamtbyte << endl;
        
        sent = send(s,buffer,strlen(buffer),0);
        if(sent == -1){
          cout << "Senden fehlgeschlagen!" << endl;
        }
        cout << sent << endl;
        
        byte = recv(s,rbuffer,BUFFER_SIZE+1024,0); 
        gesamtbyte += byte;
        cout << byte << " " << rbuffer << endl;
        count++;
      }
Wenn ich das richtig sehe, machst du dauernd neue GET-Requests. Den brauchst du nur einmal zu Anfang machen. Danach immer schön receiven.
Dabei kannst du auch gleich die empfangenen Daten überprüfen, ob "\r\n\r\n" eingegangen ist, da es das Ende des Headers kennzeichnet.
Code:
while( (byte=(recv(...))) != 0) {
//Prüfen, ob "\r\n\r\n" eingegangen
//wenn ja: Daten in Datei Schreiben
//Buffer leeren
// ...
}
Sobald der Server damit fertig ist, dir die Datei zu schicken, wird recv automatisch 0 zurückgeben. Glaub ich jedenfalls, habe immer eine andere Socket lib benutzt, wird aber wohl das Selbe sein... ;)
 
Zuletzt bearbeitet:
Wenn ich das richtig sehe, machst du dauernd neue GET-Requests. Den brauchst du nur einmal zu Anfang machen. Danach immer schön receiven.

gilt das auch dann, wenn ich die Range angebe und trotzdem die ganze Datei haben will?

@Inliferty:
Das ist zwar für Windows, aber ich mag diese API-Funktion nicht, weil sie meines Wissens nach den IE auf dem Rechner benötigt.
Und naja.. ich würde es halt gerne selbst gemacht haben :)
 
au, die Range habe ich übersehen. Aber mit Range's hab ich das noch nie gemacht. :(
Ich denke aber, dass das auch nicht notwendig ist, wenn du eine Datei am Stück laden willst. Es sei denn, du möchtest eine Funktion zum Download fortsetzen.

Sonst prüf mal, ob die Teilabschnitte der Datei richtig ankommen. Kann dir bei Binärdateien nur wieder den Hex-Editor ans Herz legen...
 
Sonst prüf mal, ob die Teilabschnitte der Datei richtig ankommen. Kann dir bei Binärdateien nur wieder den Hex-Editor ans Herz legen...

Danke auch für den Tipp, so hatte ich es vor. Nur ist das Problem, dass ich nichts zum prüfen habe, weil ich gar keine Antwort bekomme - nichtmal ein 400 Bad Request.
 
Einblick in die Logs des Servers habe ich leider nicht.

Allerdings kommt der HEAD-Request zu beginn ja auch an..
 
Zurück
Oben