2008-09-17 6 views
12

Il semble que les méthodes de Net :: HTTP de Ruby sont tout ou rien quand il s'agit de lire le corps d'une page web. Comment puis-je lire, disons, juste les 100 premiers octets du corps?Comment lire seulement x nombre d'octets du corps en utilisant Net :: HTTP?

J'essaie de lire à partir d'un serveur de contenu qui renvoie un court message d'erreur dans le corps de la réponse si le fichier demandé n'est pas disponible. J'ai besoin de lire suffisamment de corps pour déterminer si le fichier est là. Les fichiers sont énormes, donc je ne veux pas que tout le corps vérifie si le fichier est disponible.

Répondre

-3

Vous ne pouvez pas. Mais pourquoi avez-vous besoin de? Sûrement si la page dit juste que le fichier n'est pas disponible alors ce ne sera pas une page énorme (c'est-à-dire par définition, le fichier ne sera pas là)?

+0

ce n'est pas une réponse. C'est vous qui n'arrivez pas à imaginer comment quelque chose pourrait être nécessaire/utile/souhaitable parce que vous ne l'avez pas encore rencontré personnellement. Qui se soucie pourquoi il a besoin de? Qui se soucie si vous avez une fin de course autour de la question? La question est "Comment lire seulement x nombre d'octets du corps en utilisant Net :: HTTP? ". Savez-vous comment? Si non, pourquoi gaspillez-vous la bande passante de tout le monde? –

2

Etes-vous sûr que le serveur de contenu ne renvoie qu'une courte page d'erreur?

-t-il pas aussi régler la HTTPResponse à quelque chose comme approprié 404. Dans ce cas, vous pouvez intercepter l'exception dérivée HTTPClientError (très probablement HTTPNotFound) qui est soulevée lors de l'accès Net::HTTP.value().

Si vous obtenez une erreur, votre fichier n'est pas là si vous obtenez 200 le fichier commence à télécharger et vous pouvez fermer la connexion.

2

Pour lire le corps d'une requête HTTP en morceaux, vous aurez besoin d'utiliser Net::HTTPResponse#read_body comme ceci:

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

J'ai essayé cela. request_get veut toujours télécharger le fichier entier avant de traiter le bloc. – bvanderw

+0

Cela fonctionne pour moi pour les réponses groupées (avec 'Transfer-Encoding: chunked'), si j'ajoute aussi un' break' dans les deux blocs (avant les deux 'end's) pour arrêter après avoir obtenu le premier morceau. Dans ce cas, en utilisant le bloc avec 'read_body', Ruby ne lit PAS la réponse complète (et ne l'attend même pas). Mais, encore une fois: ma réponse est fragmentée pour commencer, et ce sont de petits morceaux. Je doute que HTTP autorise un client à demander explicitement une réponse en bloc, ni lui permettre de suggérer une taille de bloc maximale; il semble que l'en-tête 'Range' devrait être utilisé à la place si le serveur ne retourne pas de (petits) morceaux. – Arjan

12

Vous ne devriez pas utiliser juste un HTTP HEAD demande (méthode Ruby Net::HTTP::Head) pour voir si la ressource est là, et ne continue que si vous obtenez une réponse 2xx ou 3xx? Cela suppose que votre serveur est configuré pour renvoyer un code d'erreur 4xx si le document n'est pas disponible. Je dirais que c'était la bonne solution. Une alternative est de demander la tête HTTP et d'examiner la valeur de l'en-tête content-length dans le résultat: si votre serveur est correctement configuré, vous devriez facilement pouvoir faire la différence de longueur entre un court message et un long document. Une autre alternative: définissez le champ d'en-tête content-range dans la requête (ce qui suppose à nouveau que le serveur se comporte correctement WRT la spécification HTTP).

Je ne pense pas que la résolution du problème dans le client après vous avez envoyé la requête GET est la voie à suivre: à ce moment, le réseau a fait le gros du travail, et vous ne serez pas vraiment enregistrer les ressources gaspillées.

Référence: http header definitions

+1

Essayé que, le serveur envoie une réponse OK et un 0 pour la longueur du contenu. C'est le serveur P4Web de Perforce. – bvanderw

+3

Hmm. Si votre fournisseur envoie 200 OK alors que cela signifie vraiment que 404 n'est pas trouvé, alors vous devriez faire un bugrep prioritaire avec eux! –

+0

L'utilisation de 'HEAD' est la bonne façon d'aller du côté client. Si leur serveur est cassé, ils doivent le réparer. Malheureusement, cela ne rend pas la tâche OP plus facile, car les entreprises et les fournisseurs ne se soucient généralement pas de ce que quelqu'un qui utilise le contenu rencontre lors du piratage de leur serveur. –

3

Je voulais faire une seule fois, et la seule chose que je pouvais penser est rapiéçage singe les méthodes Net::HTTP#read_body et Net::HTTP#read_body_0 d'accepter un paramètre de longueur, puis dans l'ancienne passe juste la longueur paramètre à la méthode read_body_0, où vous pouvez lire autant que la longueur des octets.

+0

S'il vous arrive d'avoir encore du code pour cela, j'aimerais le voir. –

+0

Malheureusement je ne l'ai pas à portée de main, mais c'était assez simple, car j'avais juste besoin de lire ces octets, et je me fichais des octets suivants. J'ai donc ajouté un autre paramètre à #read_body avec le défaut 'nil', et dans # read_body_0 j'ai ajouté le paramètre len = nil et j'avais quelque chose comme: if len; @ socket.read len, dest; revenir; fin – Roman

12

Ceci est un ancien fil de discussion, mais la question de savoir comment lire seulement une partie d'un fichier via HTTP dans Ruby est encore la plus souvent sans réponse selon mes recherches. Voici une solution je suis venu avec par singe-patcher Net :: HTTP un peu:

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 

Le sauvetage attrape le IOError qui est jeté lorsque vous appelez HTTP.finish prématurément.

Pour votre information, la prise dans l'objet HTTPResponse est pas un vrai objet IO (il est une classe interne appelée BufferedIO), mais il est assez facile de singe patch qui, aussi, pour imiter les méthodes IO dont vous avez besoin. Par exemple, une autre bibliothèque que j'utilisais (exifr) avait besoin de la méthode readchar, qui était facile d'ajouter:

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

Attention, la réponse pourrait être compressée, et alors on obtiendrait 100 octets "binaires" dans l'exemple ci-dessus. Lorsque vous attendez du texte, utilisez 'request = Net :: HTTP :: Get.new (uri.request_uri, {'Accept-Encoding' => 'entity'})' pour désactiver la compression. Et notez que si la réponse est fragmentée, [on obtiendra d'abord une ligne avec la taille du morceau] (http://en.wikipedia.org/wiki/Chunked_transfer_encoding#Format), en hexadécimal. Ainsi, 'response.socket.read (100)' donnera une ligne avec le nombre hexadécimal '64', et une autre ligne avec 100 octets (ou plusieurs lignes avec des tailles plus petites, si le serveur envoie des morceaux plus petits). – Arjan

Questions connexes