2008-09-17 2 views
12

Es scheint, als ob die Methoden von Rubys Net :: HTTP alles oder nichts sind, wenn es darum geht, den Körper einer Webseite zu lesen. Wie kann ich sagen, nur die ersten 100 Bytes des Körpers?Wie lese ich nur x Anzahl der Bytes des Körpers mit Net :: HTTP?

Ich versuche, von einem Inhaltsserver zu lesen, der eine kurze Fehlermeldung im Hauptteil der Antwort zurückgibt, wenn die angeforderte Datei nicht verfügbar ist. Ich muss genug vom Körper lesen, um festzustellen, ob die Datei da ist. Die Dateien sind riesig, daher möchte ich nicht den ganzen Körper untersuchen, nur um zu prüfen, ob die Datei verfügbar ist.

Antwort

-3

Sie können nicht. Aber warum musst du? Sicher, wenn die Seite nur sagt, dass die Datei nicht verfügbar ist, dann wird es keine große Seite sein (d. H. Per Definition wird die Datei nicht da sein)?

+0

dies ist keine Antwort. Sie können sich nicht vorstellen, wie etwas notwendig/nützlich/wünschenswert sein könnte, weil Sie es persönlich noch nicht kennengelernt haben. Wen kümmert es, warum er muss? Wen kümmert es, wenn Sie ein Ende der Frage haben? Die Frage ist "Wie lese ich nur x Anzahl der Bytes des Körpers mit Net :: HTTP? ". Weißt du wie? Wenn nicht, warum verschwendest du die Bandbreite von jedem? –

2

Sind Sie sicher, dass der Inhaltsserver nur eine kurze Fehlerseite zurückgibt?

Ist es nicht gesetzt auch die HTTPResponse etwas angemessen wie 404. In diesem Fall können Sie fangen die HTTPClientError abgeleitete Ausnahme (höchstwahrscheinlich HTTPNotFound), die ausgelöst wird, wenn Net::HTTP.value() erreichbar.

Wenn Sie einen Fehler erhalten, dann war Ihre Datei nicht da, wenn Sie 200 bekommen, beginnt die Datei zu downloaden und Sie können die Verbindung schließen.

2

Um den Körper einer HTTP-Anforderung in Stücke zu lesen, müssen Sie Net::HTTPResponse#read_body wie folgt verwenden:

http.request_get('/large_resource') do |response| 
    response.read_body do |segment| 
    print segment 
    end 
end 
+2

Versucht dies. request_get möchte immer noch die gesamte Datei herunterladen, bevor der Block verarbeitet wird. – bvanderw

+0

Dies funktioniert für chunked Antworten (mit 'Transfer-Encoding: chunked'), wenn ich auch einen' break' in beiden Blöcken (vor beiden 'end's) hinzufüge, um nach Erhalt des ersten Chunks zu stoppen. In diesem Fall wird der Block mit 'read_body' verwendet, damit Ruby NICHT die volle Antwort liest (und nicht einmal darauf wartet). Aber, wieder: meine Antwort ist von Anfang an chunked, und das sind kleine Brocken. Ich bezweifle, dass HTTP einem Client erlaubt, eine Chunked-Response explizit anzufordern, und es ihm auch nicht erlaubt, eine maximale Chunk-Größe vorzuschlagen; Es scheint, dass der 'Range'-Header stattdessen verwendet werden sollte, wenn der Server keine (kleinen) Chunks zurückgibt. – Arjan

12

Sie zu sehen nur Sollte nicht eine HTTP-Anfrage HEAD (Ruby-Net::HTTP::Head-Methode) verwenden, wenn die Ressource ist da und geht nur weiter, wenn Sie eine 2xx oder 3xx Antwort bekommen? Dies setzt voraus, dass Ihr Server so konfiguriert ist, dass er einen 4xx-Fehlercode zurückgibt, wenn das Dokument nicht verfügbar ist. Ich würde argumentieren, das war die richtige Lösung. Eine Alternative ist, den HTTP-Kopf anzufordern und den Header-Wert im Ergebnis zu betrachten: Wenn Ihr Server korrekt konfiguriert ist, sollten Sie leicht in der Lage sein, den Längenunterschied zwischen einer kurzen Nachricht und einem langen Dokument zu unterscheiden. Eine weitere Alternative: Setzen Sie in der Anfrage das Header-Feld content-range (das wiederum davon ausgeht, dass sich der Server korrekt verhält, WRT die HTTP-Spezifikation).

Ich glaube nicht, dass nach im Client das Problem zu lösen Sie die GET-Anforderung gesendet haben den Weg zu gehen ist: von dieser Zeit hat sich das Netzwerk das Heben schwerer Lasten getan, und Sie werden nicht wirklich Sparen Sie Ressourcen.

Referenz: http header definitions

+1

Versucht, dass der Server eine OK-Antwort und eine 0 für Inhaltslänge sendet. Dies ist der P4Web-Server von Perforce. – bvanderw

+3

Hmm. Wenn Ihr Lieferant 200 OK sendet, wenn es wirklich bedeutet, dass 404 nicht gefunden wurde, sollten Sie einen Prioritäts-Bugrep mit ihnen auslösen! –

+0

Die Verwendung von 'HEAD' ist der korrekte Weg von der Client-Seite. Wenn ihr Server defekt ist, müssen sie es beheben. Leider macht dies die Aufgabe des OPs jedoch nicht einfacher, da es Unternehmen und Anbietern im Allgemeinen egal ist, was jemand, der den Inhalt nutzt, antrifft, wenn er seinen Server hackt. –

3

ich einmal, dies tun wollte, und das einzige, was ich von ist Affe denken konnte die Net::HTTP#read_body und Net::HTTP#read_body_0 Methoden Patchen einen Längenparameter zu akzeptieren, und dann in der ehemaligen passieren nur die Länge Parameter auf die read_body_0 Methode, wo Sie nur so viel wie Länge Bytes lesen können.

+0

Wenn Sie noch Code dafür haben, würde ich es gerne sehen. –

+0

Leider habe ich es nicht praktisch, aber es war ziemlich einfach, da ich nur diese Bytes lesen musste, und mir die folgenden Bytes egal waren. Also habe ich einen weiteren Parameter zu #read_body mit 'nil' default hinzugefügt, und in # read_body_0 habe ich den Parameter len = nil hinzugefügt und ich hatte etwas in der Art von: if len; @ socket.read len, dest; Rückkehr; ende – Roman

12

Dies ist ein alter Thread, aber die Frage, wie man nur einen Teil einer Datei via HTTP in Ruby lesen kann, ist nach meinen Recherchen immer noch eine weitgehend unbeantwortete. Hier ist eine Lösung, die ich mit von Affen-Patching Net kam :: HTTP ein bisschen:

require 'net/http' 

# provide access to the actual socket 
class Net::HTTPResponse 
    attr_reader :socket 
end 

uri = URI("http://www.example.com/path/to/file") 
begin 
    Net::HTTP.start(uri.host, uri.port) do |http| 
    request = Net::HTTP::Get.new(uri.request_uri) 
    # calling request with a block prevents body from being read 
    http.request(request) do |response| 
     # do whatever limited reading you want to do with the socket 
     x = response.socket.read(100); 
     # be sure to call finish before exiting the block 
     http.finish 
    end 
    end 
rescue IOError 
    # ignore 
end 

Die Rettung fängt den IOError, die ausgelöst wird, wenn Sie HTTP.finish vorzeitig rufen.

FYI, die Buchse innerhalb des HTTPResponse Objekts ist kein echtes IO Objekt (es ist eine interne Klasse BufferedIO genannt), aber es ist ziemlich leicht zu Affen-Patch, der auch die IO Methoden nachzuahmen Sie benötigen. Zum Beispiel habe ich eine andere Bibliothek wurde unter Verwendung (exifr) benötigt, um die readchar Methode, die hinzuzufügen war leicht zu:

class Net::BufferedIO 
    def readchar 
    read(1)[0].ord 
    end 
end 
+2

Beachten Sie, dass die Antwort komprimiert werden könnte, und dann würde man im obigen Beispiel 100 "binäre" Oktette erhalten. Wenn Sie Text erwarten, verwenden Sie 'request = Net :: HTTP :: Get.new (uri.request_uri, {'Accept-Encoding' => 'entity'})', um die Komprimierung zu deaktivieren. Und beachte, dass, wenn die Antwort chunked ist, [man erhält zuerst eine Zeile mit der Chunk-Größe] (http://en.wikipedia.org/wiki/Chunked_transfer_encoding#Format), in Hex. So ergibt 'response.socket.read (100)' eine Zeile mit der Hexadezimalzahl '64' und eine weitere Zeile mit 100 Oktetts (oder mehrere Zeilen mit kleineren Größen, wenn der Server kleinere Stücke sendet). – Arjan

Verwandte Themen