当我尝试访问Prawn生成的PDF时,为什么Net :: HTTP超时?

时间:2011-12-14 19:42:18

标签: ruby pdf ruby-on-rails-3.1 prawn net-http

我正在使用Prawn从我的控制器生成PDF,当直接在网址上访问时,它可以完美地运行,I.E。本地主机:3000 /响应/次数1.pdf

但是,当我尝试动态生成此文件以包含在Mailer中时,所有内容都会冻结并且超时。

我已尝试过各种方法来生成/附加文件,但没有一种方法改变了结果。

我也尝试修改Net :: HTTP的超时无效,只需要LONGER超时。

如果我在Rails控制台上运行此命令,则会收到PDF数据流。

Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000)

但如果我在控制器中包含此代码,则会超时。

我尝试了两种不同的方法,并且都反复失败。

方法1 控制器:

http = Net::HTTP.new('localhost', 3000)
http.read_timeout = 6000
file = http.get(response_path(@response, :format => 'pdf')) #timeout here
ResponseMailer.confirmComplete(@response,file).deliver #deliver the mail!

方法1梅勒:

def confirmComplete(response,file)
  email_address = response.supervisor_id
  attachments["test.pdf"] = {:mime_type => "application/pdf", :content=> file}
  mail to: email_address, subject: 'Thank you for your feedback!'
end

以上代码超时。

方法2控制器:

ResponseMailer.confirmComplete(@response).deliver #deliver the mail!

方法2梅勒:

def confirmComplete(response)
email_address = response.supervisor_id
attachment "application/pdf" do |a|
    a.body = Net::HTTP.get('127.0.0.1',"/responses/1.pdf", 3000) #timeout here
    a.filename = "test.pdf" 
end
mail to: email_address, subject: 'Thank you for your feedback!'

如果我切换a.body和a.filename,它首先会出错

undefined method `filename=' for #<Mail::Part:0x007ff620e05678>

我发现的每个示例都有不同的语法或建议,但没有解决Net :: HTTP超时的问题。 Rails 3.1,Ruby 1.9.2

2 个答案:

答案 0 :(得分:2)

问题在于,在开发过程中,您只运行一个服务器进程,该进程正忙于生成电子邮件。该过程正在发送另一个请求(自身)以生成PDF并等待响应。对PDF的请求基本上是在服务器上排队,以便它可以获得它的PDF,但服务器正忙于生成电子邮件并等待获取PDF才能完成。因此,你永远等待。

您需要做的是启动第二个服务器进程...

script/rails server -p 3001

然后通过类似......

获取PDF
args = ['127.0.0.1','/responses/1.pdf']
args << 3001 unless Rails.env == 'production'
file = Net::HTTP.get(*args)

顺便说一句,根据您在生产计算机上运行的服务器,您可能会遇到指向127.0.0.1的问题。您可能需要在生产中将其设置为动态并指向完整域,但这应该很容易。

答案 1 :(得分:1)

我同意https://stackoverflow.com/users/811172/jon-garvin的分析,即您只运行一个服务器进程,但我会提到另一个解决方案。重构您的PDF生成,以便您不依赖于您的控制器。

如果你正在使用Prawnto,我猜你有一个像

这样的观点
# app/views/response.pdf.prawn
pdf.text "Hello world"

将其移至Response模型:(或其他更合适的地方,如演示者)

# app/models/response.rb
require 'tmpdir'
class Response < ActiveRecord::Base
  def pdf_path
    return @pdf_path if @pdf_generated == true
    @pdf_path = File.join(Dir.tmpdir, rand(1e11).to_s)
    Prawn::Document.generate(@pdf_path) do |pdf|
      pdf.text "Hello world"
    end
    @pdf_generated = true
    @pdf_path
  end

  def pdf_cleanup
    if @pdf_generated and File.exist?(@pdf_path.to_s)
      File.unlink @pdf_path
    end
  end
end

然后在ResponsesController你可以做到:

# app/controllers/responses_controller.rb
def show
  @response = Response.find params[:id]
  respond_to do |format|
    # this sends the PDF to the browser (doesn't email it)
    format.pdf { send_file @response.pdf_path, :type => 'application/pdf', :disposition => 'attachment', :filename => 'test.pdf' }
  end
end

在您的邮件中,您可以这样做:

# this sends an email with the PDF attached
def confirm_complete(response)
  email_address = response.supervisor_id
  attachments['test.pdf'] = {:mime_type => "application/pdf", :content => File.read(response.pdf_path, :binmode => true) }
  mail to: email_address, subject: 'Thank you for your feedback!'
end

由于您是在tmpdir中创建的,因此在服务器重新启动时会自动删除它。您也可以调用清理功能。

最后一点注意事项:您可能希望使用其他型号名称,例如SupervisorReport或某些内容 - Response可能会让您在以后遇到名称空间问题