2014-02-13 83 views
1

我將我們的應用程序從3.0遷移到3.2.x.之前的流式處理是通過分配response_body一個proc來完成的。像這樣:如何在Rails 3.2中流式傳輸大型XML?

self.response_body = proc do |response, output| 
    target_obj = StreamingOutputWrapper.new(output) 
    lib_obj.xml_generator(target_obj) 
end 

正如你可以想像,在StreamingOutputWrapper響應<<

這種方式在Rails 3.2.x中已棄用。建議的方法是分配一個響應each的對象。

我現在面臨的問題是讓lib_obj.xml_generator意識到每個問題。

當前版本的它看起來像這樣:

def xml_generator(target, conditions = []) 
    builder = Builder::XmlMarkup.new(:target => target) 
    builder.root do 
    builder.elementA do 
     Model1.find_each(:conditions => conditions) { |model1| target << model1.xml_chunk_string } 
    end 
    end 
end 

其中targetStreamingOutputWrapper對象。

問題是,如何修改代碼 - xml_generator和控制器代碼,以正確地生成響應xml流。

重要的東西:由於模型記錄非常龐大,所以在內存中構建xml不是一個選項。 xml響應的典型大小約爲150MB。

+0

SAX解析你進去看了Saxerat或寶石?它完全規則並使SAX解析變得簡單易行。 –

回答

0

你在找什麼是SAX解析。 SAX一次讀取文件「塊」,而不是將整個文件加載到DOM中。這非常方便,幸運的是,有很多人想要做同樣的事情。 Nokogiri提供了XML :: SAX方法,但它在災難性文檔和語法上會變得非常混亂,這是一團糟。我會建議尋找一些位於Nokogiri之上的東西,讓你的工作更加簡單。

這裏有幾個選項 -


SAX_stream:在sax_stream

映射出的對象是超級簡單:

require 'sax_stream/mapper' 

class Product 
    include SaxStream::Mapper 

    node 'product' 
    map :id,    :to => '@id' 
    map :status,   :to => '@status' 
    map :name_confirmed, :to => 'name/@confirmed' 
    map :name,   :to => 'name' 
end 

,並調用解析器也很簡單:

require 'sax_stream/parser' 
require 'sax_stream/collectors/naive_collector' 

collector = SaxStream::Collectors::NaiveCollector.new 
parser = SaxStream::Parser.new(collector, [Product]) 

parser.parse_stream(File.open('products.xml')) 

然而,隨着收藏者的工作(或寫你自己的),最終略微混亂,所以我真的去:

Saxerator

Saxerator得到這份工作DOEN並有一些非常方便的方法遍歷到比sax_stream稍微複雜的節點。 Saxerator還有幾個非常棒的配置選項,這些配置選項都有詳細記錄。下面簡單Saxerator例如:

parser = Saxerator.parser(File.new("rss.xml")) 

parser.for_tag(:item).each do |item| 
    # where the xml contains <item><title>...</title><author>...</author></item> 
    # item will look like {'title' => '...', 'author' => '...'} 
    puts "#{item['title']}: #{item['author']}" 
end 

# a String is returned here since the given element contains only character data 
puts "First title: #{parser.for_tag(:title).first}" 

如果你最終不得不從外部源拉XML(或者是越來越頻繁更新,你不希望有在更新版本的服務器手動,檢查出THIS QUESTION和接受的答案,它的偉大工程

+0

我不認爲我需要Saxerator,因爲我沒有閱讀已經存在的xml文件。從控制器動作中,我想渲染一個使用builder動態構建的xml。而且由於xml嵌入了數千條數據庫記錄,所以我想通過Builder **構建它。 –

+0

saxerator也會寫入文件,這實際上是我大部分時間使用的文件。 –

0

你總是可以猴子補丁響應對象:?

response.stream.instance_eval do 
    alias :<< :write 
end 
builder = Builder::XmlMarkup.new(:target => response.stream) 
... 
+0

我試過這個.....不行。 ActionDispatch :: Response :: Buffer @buf擁有所有的xml標記,但它從未被刷新過。 Rails 4.2.x與Ruby 2.1.7。客戶端之間的通訊<-> rails應用程序1 <-> rails應用程序2 <-> s3。我有xml流式傳輸,但在客戶端<-> rails應用程序1之間,我需要翻譯或處理XML。 Saxerator不會進行大塊讀取,但是當我構建新的XML時,它不會將其傳輸到客戶端。 – GregD