Ruby on Rails 3: Transmitindo dados através do Rails para o cliente DoCow
arquivos de nuvem Ruby on Rails ruby on rails 3 streaming de
Recomendação de utilidade clone profunda Diferença de desempenho entre uma importação de curinga e a importação de class necessária As variables ​​não inicializadas do C # são perigosas? Como inserir uma nova linha na frente de um padrão? Parâmetro de consulta de pesquisa opcional do Mongoose? como diferenciar evento de clique único e evento de duplo clique? Melhor maneira de encontrar controle no ASP.NET Biblioteca SSH para Java O Async está aguardando palavra-chave equivalente a um ContinueWith lambda? Eu continuo recebendo "Untaught SyntaxError: token inesperado o" RegEx para se certificar de que a string contém pelo menos um caractere minúsculo, caractere maiúsculo, dígito e símbolo C ++ obter milissegundos de tempo no Linux – relógio () não parece funcionar corretamente Uso de palavra-chave virtual + substituição vs. nova loop através de um object (tree) recursivamente Imprimindo todas as variables ​​de valor de uma class
Eu estou trabalhando em um aplicativo Ruby on Rails que se comunica com cloudfiles RackSpace (semelhante ao Amazon S3, mas faltando alguns resources).
Devido à falta de disponibilidade de permissions de access por object e autenticação de cadeia de consulta, os downloads para os usuários precisam ser mediados por meio de um aplicativo.
No Rails 2.3, parece que você pode construir dinamicamente uma resposta da seguinte maneira:
(de http://api.rubyonrails.org/classs/ActionController/Base.html#M000464 )
Em vez de 10_000_000.times... eu poderia despejar meu código de geração de stream cloudfiles lá.
O problema é que esta é a saída que recebo quando tento usar essa técnica no Rails 3.
Parece que talvez o método de call do object proc não esteja sendo chamado? Alguma outra ideia?
Parece que isso não está disponível no Rails 3
Isso pareceu funcionar para mim no meu controlador:
Atribuir a response_body um object que responde a #each :
Se você estiver usando o 1.9.x ou o Backports gem, você pode escrever isto de forma mais compacta usando Enumerator.new :
Observe que quando e se os dados forem liberados depende do manipulador do Rack e do servidor subjacente que está sendo usado. Confirmei que o Mongrel, por exemplo, transmitirá os dados, mas outros usuários relataram que o WEBrick, por exemplo, armazena em buffer até que a resposta seja fechada. Não há como forçar a resposta a liberar.
No Rails 3.0.x, existem várias dicas adicionais:
No modo de desenvolvimento, fazer coisas como acessar classs de modelo de dentro da enumeração pode ser problemático devido a interações ruins com o recarregamento de classs. Este é um bug aberto no Rails 3.0.x.
Um bug na interação entre o Rack e o Rails faz com que #each seja chamado duas vezes para cada solicitação. Este é outro bug aberto . Você pode contorná-lo com o seguinte patch de macaco:
Ambos os problemas são corrigidos no Rails 3.1, onde o streaming HTTP é um recurso de seleção.
Note que a outra sugestão comum, self.response_body = proc {|response, output| ...} self.response_body = proc {|response, output| ...} , funciona no Rails 3.0.x, mas foi preterido (e não irá mais transmitir os dados) em 3.1. Atribuindo um object que responde a #each trabalha em todas as versões do Rails 3.
Graças a todos os posts acima, aqui está um código totalmente funcional para transmitir grandes CSVs. Este código:
Não requer gemas adicionais.
Usa Model.find_each () para não sobrecarregar a memory com todos os objects correspondentes.
Foi testado em rails 3.2.5, ruby ​​1.9.3 e heroku usando unicórnio, com dyno único.
Adiciona um GC.start a cada 500 linhas, para não explodir a memory permitida do heroku dyno.
Você pode precisar ajustar o GC.start dependendo do tamanho da memory do seu modelo. Eu tenho usado com sucesso isso para transmitir modelos de 105K em um csv de 9,7MB sem quaisquer problemas.
Método do Controlador:
Caso você esteja atribuindo ao response_body um object que responda ao método #each e esteja em buffer até que a resposta seja fechada, tente no controlador de ação:
Apenas para o registro, rails> = 3.1 tem uma maneira fácil de transmitir dados, atribuindo um object que responde ao método #each para a resposta do controlador.
Tudo é explicado aqui: http://blog.sparqcode.com/2012/02/04/streaming-data-with-rails-3-1-or-3-2/
Sim, response_body é a maneira do Rails 3 de fazer isso no momento: https://rails.lighthouseapp.com/projects/8994/tickets/4554-render-text-proc-regression
Isso resolveu meu problema também – eu tenho arquivos CSV gzip'd, quero enviar para o usuário como CSV descompactado, então eu os leio uma linha de cada vez usando um GzipReader.
Essas linhas também são úteis se você estiver tentando entregar um arquivo grande como download:
Além disso, você terá que definir o header 'Content-Length' por sua auto.
Caso contrário, o Rack terá que esperar (armazenando dados do corpo na memory) para determinar o comprimento. E isso arruinará seus esforços usando os methods descritos acima.
No meu caso, eu poderia determinar o comprimento. Nos casos em que você não pode, você precisa fazer o Rack começar a enviar o corpo sem o header 'Content-Length' . Tente adicionar no config.ru "use Rack :: Chunked" depois de "require" antes do "run". (Obrigado arkadiy)
Comentei no ingresso do farol, só queria dizer que a abordagem self.response_body = proc funcionou para mim, embora eu precisasse usar o Mongrel em vez do WEBrick para ter sucesso.
Aplicar a solução de John junto com a sugestão de Exequiel funcionou para mim.
marca a resposta como não-armazenável em rack.
Depois de investigar mais, descobri que também se poderia usar isso:
Isso, para mim, é apenas um pouco mais intuitivo. Ele transmite a mensagem para qualquer outra pessoa que esteja lendo meu código. Além disso, no caso de uma futura versão do rack parar de verificar Last-Modified, muito código pode quebrar e pode demorar um pouco para que as pessoas descubram o porquê.