0

Roo gem を使用して、Excel ファイル、CSV ファイルなどをインポートしています。

このコードは、a) 遅延ジョブの有無にかかわらず私のローカル ボックスで、b) 遅延ジョブのない Heroku でうまく機能します。残念ながら、遅延ジョブを使用している場合、Heroku で実行すると失敗します。

詳細はこちら。

コントローラ:

def importdo
  fileimport = Fileimport.new
  fileimport.file_name = params[:file].original_filename
  fileimport.file_path = params[:file].path
  fileimport.save
  fileimportid = fileimport.id
  Building.delay.import(fileimportid)
  redirect_to buildings_path, notice: "Buildings import started . . . you will receive an email upon completion."
end

fileimportは、ファイル名とファイルパスを追跡するために使用する単なるテーブルです (これらをパラメーターとして渡そうとしましたが、ハッシュを YAML に変換しようとすると DelayedJob に常に問題が発生するため、代わりにレコード ID を渡します)。これにより、インポートをテーブルで追跡できるという利点も得られます。

これが私のインポートクラスメソッドです:

def self.import(fileimportid)
    fileimport = Fileimport.find(fileimportid)
    file_name = fileimport.file_name
    file_path = fileimport.file_path
    newcount = 0
    updatecount = 0
    updating = false
    Building.acts_as_gmappable :process_geocoding => false
    spreadsheet = open_spreadsheet(file_name, file_path)
    header = spreadsheet.row(1)
    (2..spreadsheet.last_row).each do |i|
        row = Hash[[header, spreadsheet.row(i)].transpose]

        if zip = row["zip"].to_i.to_s
            if zip.length > 5
                zip = zip.first(5)
            end
        else
            raise "zip not valid"
        end

        if building = find_by_address_and_zip(row["address"], zip)
            updating = true
        else
            building = Building.new
        end
        #building.name = row["name"]
        building.zip = zip
        building.address = row["address"]
        building.city = row["city"]
        building.save!
        if updating
            updatecount += 1
        else
            newcount += 1
        end
        updating=false
    end
    Building.acts_as_gmappable :process_geocoding => true

    subject = "Your Building Import Completed Successfully!"
    body = "Your Building Import completed successfully!\n\n" + newcount.to_s + " buildings were created.\n" + updatecount.to_s + " buildings were updated."

    require 'rest_client'

    RestClient.post MAILGUN_API_URL+"/messages", :from => "obfuscated", :to => "obfuscated", :subject => subject, :text => body 

end

def self.open_spreadsheet(file_name, file_path)
    case File.extname(file_name)
        when ".csv" then Roo::Csv.new(file_path, nil, :ignore)
        when ".xls" then Roo::Excel.new(file_path, nil, :ignore)
        when ".xlsx" then Roo::Excelx.new(file_path, nil, :ignore)
    else
        raise "Unknown file type: #{file_name}"
    end
end

ファイル入力のフォームは次のとおりです。

<% provide(:title, 'Import Buildings') %>
<div class="row">
    <aside class="span4">
        <section>
            <h1>
                Import Products
            </h1>
            <%= form_tag importdo_buildings_path, multipart: true do %>
                <%= file_field_tag :file %>
                <%= submit_tag "Import" %>
            <% end %>
        </section>
    </aside>
</div>

ルートは次のようになります。

resources :buildings do
    collection { post :importdo }
end

とにかく、私が言ったように、私のボックスでうまく機能します(遅延ジョブの有無にかかわらず)。遅延ジョブなしで Heroku でのみ動作します (つまり、.delay を取り出す必要があります)。これが私が得ている特定のエラーです(エラーは私に電子メールで送信されます。オブザーバーを使用して、Delayed::Jobがレコードを保存し、last_errorをチェックするタイミングを確認します):

file /tmp/RackMultipart20130516-13-7li3o7 does not exist
/app/vendor/bundle/ruby/1.9.1/gems/roo-1.11.2/lib/roo/excel.rb:26:in `block in initialize'
/app/vendor/bundle/ruby/1.9.1/gems/roo-1.11.2/lib/roo/generic_spreadsheet.rb:579:in `block in make_tmpdir'
/app/vendor/ruby-1.9.3/lib/ruby/1.9.1/tmpdir.rb:83:in `mktmpdir'
/app/vendor/bundle/ruby/1.9.1/gems/roo-1.11.2/lib/roo/generic_spreadsheet.rb:578:in `make_tmpdir'
/app/vendor/bundle/ruby/1.9.1/gems/roo-1.11.2/lib/roo/excel.rb:19:in `initialize'
/app/app/models/building.rb:84:in `new'
/app/app/models/building.rb:84:in `open_spreadsheet'
/app/app/models/building.rb:39:in `import'

私の最初の考えでは、Heroku ファイルシステムのことだけだと思っていましたが、delayed_job がなければ機能しないと思いました。私のもう1つの考えは、非同期であるため、タイミングの問題である可能性があります(一時ファイルがまだ存在しないか、もう存在しない可能性があります)。

どんなアイデアでも大歓迎です。

4

1 に答える 1

0

あなたの最初の考えは正しかった..問題の背後にあるのはHerokuファイルシステムです。つまり、読み取り専用ルールです。

ファイルを Heroku に送信すると、ファイルは一時フォルダに保存され、この 1 回のリクエストでのみ使用できます。1回のリクエスト中にジョブを実行し、ファイルが利用可能であるため、delayed_jobなしで機能します。

ただし、リクエストが終了すると、delayed_job は個別に実行されるため、ファイルは削除されます。

最も簡単な解決策は、ファイルをクラウド ストレージに配置することです (そのために S3 と Carrierwave を使用しました。

于 2016-09-23T15:18:00.037 に答える