2010-06-12 4 views
3

Dire que j'ai une route Sinatra ala:Puis-je faire en sorte que Sinatra/Rack ne lise pas tout le corps de la requête en mémoire?

put '/data' do 
    request.body.read 
    # ... 
end 

Il semble que l'ensemble request.body est lu en mémoire. Existe-t-il un moyen de consommer le corps à mesure qu'il arrive dans le système, plutôt que d'avoir tout tamponné dans Rack/Sinatra au préalable? Je vois que je peux le faire pour lire le corps en plusieurs parties, mais tout le corps semble encore être lu dans la mémoire au préalable.

put '/data' do 
    while request.body.read(1024) != nil 
    # ... 
    end 
    # ... 
end 

Répondre

3

Vous ne pouvez pas éviter ceci en général sans patcher Sinatra et/ou Rack. Il est fait par Rack::Request lorsque request.POST est called by Sinatra pour créer params.

Mais vous pouvez placer un middleware devant Sinatra pour enlever le corps:

require 'sinatra' 
require 'stringio' 

use Rack::Config do |env| 
    if env['PATH_INFO'] == '/data' and env['REQUEST_METHOD'] == 'PUT' 
    env['rack.input'], env['data.input'] = StringIO.new, env['rack.input'] 
    end 
end 

put '/data' do 
    while request.env['data.input'].body.read(1024) != nil 
    # ... 
    end 
    # ... 
end 
+0

Konstantin - Merci pour la réponse détaillée ... Quelques questions pour vous un ou modifié deux et je vais accepter votre répondre. 1. (l'édition) Dans votre fragment de code, pouvez-vous s'il vous plaît fixer 'StrinIO.new' à' StringIO.new'? 2. Si vous avez utilisé Rack pour retirer le corps, l'ensemble du corps ne sera-t-il pas encore lu par Rack à partir du réseau en mémoire? Si c'est le cas, peut-être une petite modification à votre réponse pour le refléter? 3. Je n'ai pas vu Ruby comme 'env ['rack.input'], env ['data.input'] = StrinIO.new, env ['rack.input']' avant. Pouvez-vous expliquer dans un commentaire ce que fait ce fragment de code? Merci encore! –

+1

'a, b = 1,2' assigne 1 à a et 2 à b, donc' env ['rack.input'], env ['data.input'] = StringIO.new, env ['rack.input' ] 'définit' env ['rack.input'] 'sur un' StringIO' vide et place son ancienne valeur (probablement un descripteur de fichier temporaire) dans 'env ['data.input']' pour que vous puissiez le référencer plus tard. – BaroqueBobcat

+0

BaroqueBobca a raison. Ce pattern est particulièrement courant si vous voulez échanger les valeurs de deux variables. Imaginez que vous avez 'a' et' b' et que vous voulez mettre à la fois 'a = b' et' b = a'. Pour ne pas perdre l'ancienne valeur de 'a', vous devez créer une variable temporaire, comme' old = a; a = b; b = c'. Ruby supporte une autre syntaxe (tout comme Python, afaik): 'a, b = b, a', ce qui évite la variable temporaire, car il évalue d'abord complètement la main droite (' b, a') et ensuite l'assignation. –

Questions connexes