0

我有一個相當簡單的Rails應用程序在Heroku上運行,最終用戶可以上傳兩張照片並通過電子郵件和短信接收到照片的鏈接。異步jQuery上傳似乎仍然上傳到Heroku

該應用程序最初實時運行Paperclip,有時因爲Heroku的30秒限制而超時。我切換到jQuery上傳和S3上傳圖像,然後在提交之前創建客戶,在事實之後使用Paperclip作爲延遲作業。這完美地工作 - 除了它繼續在#create超時,並且當我在瀏覽器的左下角看它說「上傳(XX%)」,好像它正在#create上傳已經上傳的文件。我正在拉我的頭髮。

再次,照片加載到S3。在快速連接上,這不會超時,並且一切正常。在較慢的連接上,在表單上點擊「提交」(在圖像上傳成功後)超時。

下面的代碼。非常感謝,如果有人看到我失蹤 - 爲什麼這種形式會試圖再次提交照片?

#Controller 

def create 
    @s3_direct_post = S3_BUCKET.presigned_post(key: "photos/#{SecureRandom.uuid}/${filename}", success_action_status: 201, acl: :public_read) 

if params[:customer][:sms] 
    params[:customer][:sms].gsub!(/[\s\-\(\)]+/, '') 
end 

@customer = current_salesperson.customers.build(customer_params) 

if @customer.direct_upload_photo2_url? 
    @customer.turkee_status = "ready_to_send" 
end 

respond_to do |format| 
    if @customer.save 
    @customer.queue_processing 

    unless @customer.email.blank? 
     CustomerMailer.delay.welcome_email(@customer) 
    end 

    unless @customer.sms.blank? 
     send_sms 
    end 

    if params[:fbemail] 
     CustomerMailer.post_to_facebook(@customer).deliver 
    end 
    format.html { redirect_to @customer, notice: 'Customer was successfully created.' } 
    format.json { render action: 'show', status: :created, location: @customer } 
    else 
    format.html { render action: 'new' } 
    format.json { render json: @customer.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

相關的模型

class Customer < ActiveRecord::Base 

    has_attached_file :photo, 
      :styles => { :thumb => "228x152#" }, 
      :storage => :s3, 
      :s3_credentials => { 
      :bucket => ENV['S3_BUCKET_NAME'], 
      :access_key_id => ENV['AWS_ACCESS_KEY_ID'], 
      :secret_access_key => ENV['AWS_SECRET_ACCESS_KEY'] 
      }, 
      :url => ":s3_domain_url", 
      :path => "photos/:owner_id/:id:hash:style.:extension", 
      :hash_secret => "XXX", 
      :region => "us-west-2", 
      :s3_host_name => "s3.amazonaws.com", 
      :default_url => "https://s3-us-west-2.amazonaws.com/missing.jpg" 

def queue_processing 
     Customer.delay.send_to_paperclip(id) 
     end 

形式:

<div class="form-group"> <%= form_for(@customer, :html => { class: "directUpload" }) do |f| %> <% if @customer.errors.any? %> 
    <div id="error_explanation"> 
     <h2><%= pluralize(@customer.errors.count, "error") %> prohibited this customer from being saved:</h2> 

      <% @customer.errors.full_messages.each do |msg| %> 
      <%= msg %><br /> 
      <% end %> 
    </div> <% end %> 

    <div class="form-group"> 
    <%= f.label :name, "Customer Name" %> 
    <%= f.text_field :name, class: 'form-control', :disabled => @disabled %> </div> <div class="form-group"> 
    <%= f.label :email %> 
    <%= f.text_field :email, class: 'form-control' %> </div> 

    <div class="form-group"> 
    <%= f.label :sms, 'Cell Phone # (for one-time SMS)' %> 
    <%= f.text_field :sms, class: 'form-control' %> 
    </div> 


    <span> <div class="form-group"> <%= f.label :direct_upload_photo2_url, 'Photograph the Second Part' %> <%= f.file_field :direct_upload_photo2_url, class: 'form-control' %> </div> </span> 


<script> $(function() { $('.directUpload').find("input:file").each(function(i, elem) { 
    var fileInput = $(elem); 
    var form   = $(fileInput.parents('form:first')); 
    var submitButton = form.find('button[type="submit"]'); 
    var progressBar = $("<div class='bar'></div>"); 
    var barContainer = $("<div class='progress'></div>").append(progressBar); 
    fileInput.after(barContainer); 
    fileInput.fileupload({ 
     fileInput:  fileInput, 
     url:    '<%= @s3_direct_post.url %>', 
     type:   'POST', 
     autoUpload:  true, 
     formData:   <%= @s3_direct_post.fields.to_json.html_safe %>, 
     paramName:  'file', 
     dataType:   'XML', 
     replaceFileInput: false, 
     progressall: function (e, data) { 
     var progress = parseInt(data.loaded/data.total * 100, 10); 
     progressBar.css('width', progress + '%') 
     }, 
     start: function (e) { 
     submitButton.prop('disabled', true); 

     progressBar. 
      css('background', 'green'). 
      css('display', 'block'). 
      css('width', '0%'). 
      text("Loading..."); 
     }, 
     done: function(e, data) { 
     submitButton.prop('disabled', false); 
     progressBar.text("Uploading done"); 

     // extract key and generate URL from response 
     var key = $(data.jqXHR.responseXML).find("Key").text(); 
     var url = '//<%= @s3_direct_post.url.host %>/' + key; 

     // create hidden field 
     var input = $("<input />", { type:'hidden', name: fileInput.attr('name'), value: url }) 
     form.append(input); 
     }, 
     fail: function(e, data) { 
     submitButton.prop('disabled', false); 

     progressBar. 
      css("background", "red"). 
      text("Failed"); 
     } 
    }); }); }); </script> 

    <div class="form-group"> <%= f.label :direct_upload_photo_url, 'Take or Choose a LANDSCAPE Photo' %> <%= f.file_field :direct_upload_photo_url, class: 'form-control' %> </div> 


    <div class="form-group"> 
    <span class="btn btn-default btn-file"> 
     <input id="fbemail" name="fbemail" type="checkbox" value="1"> Post to Facebook 
    </span> 
    </div> 
    <div class="actions"> 
    <%= f.button "Submit", class: 'btn btn-primary' %> </div> 


<% end %> </div> 

在日誌中的錯誤是:

Jan 29 06:14:20 myname app/web.1: Started POST "/customers" for 70.192.215.71 at 2015-01-29 14:14:19 +0000 
Jan 29 06:14:20 myname app/web.1: Parameters: {"utf8"=>"✓", "authenticity_token"=>"ypEWqX54OUBUGTm4GNtfM1QjSnqxZHB4bFcKw==", "customer"=>{"name"=>"John ", "email"=>"[email protected]", "sms"=>"206XXXXXXX", "direct_upload_photo2_url"=>"//myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg", "direct_upload_photo_url"=>"//myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg", "second_salesperson_id"=>"", "owner_id"=>"26"}, "button"=>""} 
Jan 29 06:14:20 myname app/web.1: Processing by CustomersController#create as HTML 
Jan 29 06:14:20 myname app/web.1: Salesperson Load (1.9ms) SELECT "salespeople".* FROM "salespeople" WHERE "salespeople"."id" = $1 ORDER BY "salespeople"."id" ASC LIMIT 1 [["id", 39]] 
Jan 29 06:14:20 myname app/web.1: (2.2ms) BEGIN 
Jan 29 06:14:20 myname app/web.1: SQL (3.7ms) UPDATE "salespeople" SET "customers_count" = COALESCE("customers_count", 0) + 1 WHERE "salespeople"."id" = $1 [["id", 39]] 
Jan 29 06:14:20 myname app/web.1: (0.7ms) BEGIN 
Jan 29 06:14:20 myname app/web.1: (2.7ms) COMMIT 
Jan 29 06:14:20 myname app/web.1: SQL (6.0ms) INSERT INTO "customers" ("name", "email", "owner_id", "sms", "direct_upload_photo_url", "direct_upload_photo2_url", "salesperson_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id" [["name", "John "], ["email", "[email protected]"], ["owner_id", 26], ["sms", "206XXXXXXX"], ["direct_upload_photo_url", "//myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg"], ["direct_upload_photo2_url", "//myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg"], ["salesperson_id", 39], ["created_at", "2015-01-29 14:14:20.443471"], ["updated_at", "2015-01-29 14:14:20.443471"]] 
Jan 29 06:14:20 myname app/worker.1: Delayed::Backend::ActiveRecord::Job Load (2.7ms) UPDATE "delayed_jobs" SET locked_at = '2015-01-29 14:14:20.518245', locked_by = 'host:b4e4f686-7966-4131-9609-61b493f6ca44 pid:3' WHERE id IN (SELECT "delayed_jobs"."id" FROM "delayed_jobs" WHERE ((run_at <= '2015-01-29 14:14:20.517036' AND (locked_at IS NULL OR locked_at < '2015-01-29 10:14:20.517201') OR locked_by = 'host:b4e4f686-7966-4131-9609-61b493f6ca44 pid:3') AND failed_at IS NULL) ORDER BY priority ASC, run_at ASC LIMIT 1 FOR UPDATE) RETURNING * 
Jan 29 06:14:20 myname app/web.1: (2.8ms) COMMIT 
Jan 29 06:14:20 myname app/web.1: SQL (1.2ms) INSERT INTO "delayed_jobs" ("handler", "run_at", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["handler", "--- !ruby/object:Delayed::PerformableMethod\nobject: !ruby/class 'customer'\nmethod_name: :send_to_paperclip\nargs:\n- 111\n"], ["run_at", "2015-01-29 14:14:20.521860"], ["created_at", "2015-01-29 14:14:20.522421"], ["updated_at", "2015-01-29 14:14:20.522421"]] 
Jan 29 06:14:20 myname app/web.1: (0.7ms) BEGIN 
Jan 29 06:14:20 myname app/web.1: (3.2ms) COMMIT 
Jan 29 06:14:20 myname app/web.1: SQL (3.0ms) INSERT INTO "delayed_jobs" ("handler", "run_at", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["handler", "--- !ruby/object:Delayed::PerformableMailer\nobject: !ruby/class 'CustomerMailer'\nmethod_name: :welcome_email\nargs:\n- !ruby/object:customer\n raw_attributes:\n id: '111'\n name: 'John '\n email: [email protected]\n created_at: 2015-01-29 14:14:20.443471733 Z\n updated_at: 2015-01-29 14:14:20.443471733 Z\n salesperson_id: 39\n photo_file_name: \n photo_content_type: \n photo_file_size: \n photo_updated_at: \n views: '0'\n owner_id: '26'\n photo26: \n tweet_count: '0'\n facebook_count: '0'\n photo2_file_name: \n photo2_content_type: \n photo2_file_size: \n photo2_updated_at: \n second_salesperson_id: ''\n turkee_task_id: \n turkee_status: not_sent\n make: \n model: \n year: \n sms: '206XXXXXXX'\n direct_upload_photo_url: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n direct_upload_photo2_url: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n attributes: !ruby/object:ActiveRecord::AttributeSet\n attributes: !ruby/object:ActiveRecord::LazyAttributeHash\n  types:\n  id: &3 !ruby/object:ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Integer\n   precision: \n   scale: \n   limit: \n   range: !ruby/range\n   begin: -2147483648\n   end: 2147483648\n   excl: true\n  name: &1 !ruby/object:ActiveRecord::Type::String\n   precision: \n   scale: \n   limit: 255\n  email: *1\n  created_at: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: &2 !ruby/object:ActiveRecord::ConnectionAdapters::PostgreSQL::OID::DateTime\n   precision: \n   scale: \n   limit: \n  updated_at: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n  salesperson_id: *3\n  photo_file_name: *1\n  photo_content_type: *1\n  photo_file_size: *3\n  photo_updated_at: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n  views: *3\n  owner_id: *3\n  photo26: *1\n  tweet_count: *3\n  facebook_count: *3\n  photo2_file_name: *1\n  photo2_content_type: *1\n  photo2_file_size: *3\n  photo2_updated_at: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n  second_salesperson_id: *3\n  turkee_task_id: *3\n  turkee_status: *1\n  make: *1\n  model: *1\n  year: *1\n  sms: &4 !ruby/object:ActiveRecord::Type::String\n   precision: \n   scale: \n   limit: \n  direct_upload_photo_url: *4\n  direct_upload_photo2_url: *4\n  values:\n  id: \n  name: \n  email: \n  created_at: \n  updated_at: \n  salesperson_id: \n  photo_file_name: \n  photo_content_type: \n  photo_file_size: \n  photo_updated_at: \n  views: '0'\n  owner_id: \n  photo26: \n  tweet_count: '0'\n  facebook_count: '0'\n  photo2_file_name: \n  photo2_content_type: \n  photo2_file_size: \n  photo2_updated_at: \n  second_salesperson_id: \n  turkee_task_id: \n  turkee_status: not_sent\n  make: \n  model: \n  year: \n  sms: \n  direct_upload_photo_url: \n  direct_upload_photo2_url: \n  additional_types: {}\n  materialized: true\n  delegate_hash:\n  id: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: id\n   value_before_type_cast: '111'\n   type: *3\n   value: 111\n  name: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: name\n   value_before_type_cast: 'John '\n   type: *1\n   value: 'John '\n  email: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: email\n   value_before_type_cast: [email protected]\n   type: *1\n   value: [email protected]\n  created_at: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: created_at\n   value_before_type_cast: 2015-01-29 14:14:20.443471733 Z\n   type: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n   value: 2015-01-29 14:14:20.443471733 Z\n  updated_at: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: updated_at\n   value_before_type_cast: 2015-01-29 14:14:20.443471733 Z\n   type: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n   value: 2015-01-29 14:14:20.443471733 Z\n  salesperson_id: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: salesperson_id\n   value_before_type_cast: 39\n   type: *3\n   value: 39\n  photo_file_name: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo_file_name\n   value_before_type_cast: \n   type: *1\n   value: \n  photo_content_type: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo_content_type\n   value_before_type_cast: \n   type: *1\n   value: \n  photo_file_size: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo_file_size\n   value_before_type_cast: \n   type: *3\n   value: \n  photo_updated_at: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo_updated_at\n   value_before_type_cast: \n   type: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n   value: \n  views: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: views\n   value_before_type_cast: '0'\n   type: *3\n   value: 0\n  owner_id: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: owner_id\n   value_before_type_cast: '26'\n   type: *3\n   value: 26\n  photo26: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo26\n   value_before_type_cast: \n   type: *1\n   value: \n  tweet_count: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: tweet_count\n   value_before_type_cast: '0'\n   type: *3\n   value: 0\n  facebook_count: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: facebook_count\n   value_before_type_cast: '0'\n   type: *3\n   value: 0\n  photo2_file_name: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo2_file_name\n   value_before_type_cast: \n   type: *1\n   value: \n  photo2_content_type: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo2_content_type\n   value_before_type_cast: \n   type: *1\n   value: \n  photo2_file_size: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo2_file_size\n   value_before_type_cast: \n   type: *3\n   value: \n  photo2_updated_at: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: photo2_updated_at\n   value_before_type_cast: \n   type: !ruby/object:ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter\n   subtype: *2\n   value: \n  second_salesperson_id: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: second_salesperson_id\n   value_before_type_cast: ''\n   type: *3\n   value: \n  turkee_task_id: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: turkee_task_id\n   value_before_type_cast: \n   type: *3\n   value: \n  turkee_status: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: turkee_status\n   value_before_type_cast: not_sent\n   type: *1\n   value: not_sent\n  make: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: make\n   value_before_type_cast: \n   type: *1\n   value: \n  model: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: model\n   value_before_type_cast: \n   type: *1\n   value: \n  year: !ruby/object:ActiveRecord::Attribute::FromDatabase\n   name: year\n   value_before_type_cast: \n   type: *1\n   value: \n  sms: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: sms\n   value_before_type_cast: '206XXXXXXX'\n   type: *4\n   value: '206XXXXXXX'\n  direct_upload_photo_url: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: direct_upload_photo_url\n   value_before_type_cast: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n   type: *4\n   value: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n  direct_upload_photo2_url: !ruby/object:ActiveRecord::Attribute::FromUser\n   name: direct_upload_photo2_url\n   value_before_type_cast: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n   type: *4\n   value: //myname.s3.amazonaws.com/photos/380057ca-f611-4541-80a1-6850560de612/image.jpg\n new_record: false\n"], ["run_at", "2015-01-29 14:14:20.551569"], ["created_at", "2015-01-29 14:14:20.551905"], ["updated_at", "2015-01-29 14:14:20.551905"]] 
Jan 29 06:14:20 myname app/web.1: owner Load (2.4ms) SELECT "owners".* FROM "owners" WHERE "owners"."id" = $1 LIMIT 1 [["id", 26]] 
Jan 29 06:14:21 myname app/web.1: SMS: To: +1206XXXXXXX Body: "Check out your photo at http://url/name/111" 
Jan 29 06:14:21 myname app/web.1: E, [2015-01-29T14:14:20.779161 #3] ERROR -- : worker=0 PID:6 timeout (30s > 29s), killing 
Jan 29 06:14:21 myname app/web.1: E, [2015-01-29T14:14:20.808642 #3] ERROR -- : reaped #<Process::Status: pid 6 SIGKILL (signal 9)> worker=0 
Jan 29 06:14:21 myname heroku/router: at=error code=H13 desc="Connection closed without response" method=POST path="/customers" host=www.url.com request_id=e02c7966-0e1c-44a6-b624-72c882683507 fwd="70.192.215.71" dyno=web.1 connect=1ms service=29974ms status=503 bytes=0 

回答

0

我結束了移動的jQuery上傳到自己的獨立形式在Rails創建表單上方。這固定了它。