質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

2回答

855閲覧

Tempfileで作成したPDFファイルがスクリプト実行中に削除されてしまう

ssk

総合スコア332

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

0クリップ

投稿2022/09/25 10:07

編集2022/09/26 00:31

1行のCSVデータでは問題なくzipファイルをモデルに保存できますが、複数行のCSVデータでは
No such file or directoryとなり、zipファイルをモデルに保存することができませんでした。

tmp/以下のディレクトリを実行中に見ると、勝手にPDFファイルが削除されているようでした。
以下、コードになります。ご教授いただけますと幸いです。

ruby

1class SendMaskInsuranceFile < ApplicationRecord 2 mount_uploader :csv_url, DocumentFileUploader 3 mount_uploader :zip_url, ZipFileUploader 4 5 validates :csv_url, presence: true 6 7 def self.generate_mask_insurance_zip_file 8 ActiveRecord::Base.transaction do 9 SendMaskInsuranceFile.where(created_at: Time.current.yesterday..Time.current).each do |send_mask_insurance_file| 10 Tempfile.create(["#{Time.current.to_i}", ".zip"], Rails.root.join('tmp')) do |zip_tempfile| 11 Zip::File.open(zip_tempfile.path, Zip::File::CREATE) do |zipfile| 12 URI.open(send_mask_insurance_file.csv_url.path) do |f| 13 csv = CSV.new(f, headers: :first_row) 14 csv.each do |row| 15 staff_master = StaffMaster.find_by(id: row[5]) 16 employment_file = staff_master.original_insurance_files.where(file_type: :social).order(id: :desc).first 17 social_file = staff_master.original_insurance_files.where(file_type: :employment).order(id: :desc).first 18 if staff_master 19 pdf_string = WickedPdf.new.pdf_from_string( 20 ActionController::Base.new().render_to_string( 21 template: '/admins/send_mask_insurance_files/sended.pdf.slim', 22 layout: 'layouts/pdf.pdf.slim', 23 encoding: 'utf-8', 24 locals: { 25 postal_code: row[3], 26 address: row[4], 27 company_name: row[2], 28 department_name: row[1], 29 employment_file: employment_file.blank? ? nil : embed_remote_image(employment_file.url.path, 'image/jpeg'), 30 social_file: social_file.blank? ? nil : embed_remote_image(social_file.url.path, 'image/jpeg'), 31 } 32 ) 33 ).force_encoding("UTF-8") 34 35 Tempfile.create(["#{staff_master.id}-#{Time.current.to_i}", ".pdf"], Rails.root.join('tmp')) do |mask_insurance_tempfile| 36 mask_insurance_tempfile.write pdf_string 37 zipfile.add(File.basename(mask_insurance_tempfile.path), mask_insurance_tempfile.path) 38 end 39 end 40 end 41 end 42 end 43 send_mask_insurance_file.update!(zip_url: zip_tempfile) 44 end 45 end 46 end 47 end 48 49 def self.embed_remote_image(url, content_type) 50 asset = URI.open(url, "r:UTF-8", &:read) 51 base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "") 52 "data:#{content_type};base64,#{Rack::Utils.escape(base64)}" 53 end 54end

実行コマンド

bundle exec rake cron:generate_mask_insurance_zip_file

エラーメッセージ

[START] [{:generate_mask_insurance_zip_file=>:environment}] (2022/09/26 09:28) [ERROR] No such file or directory @ rb_sysopen - /Users/●●/Dropbox/sites/app/tmp/02393164-166415211720220926-1793-w6nj56.pdf (2022/09/26 09:28) rake aborted! Errno::ENOENT: No such file or directory @ rb_sysopen - /Users/●●/Dropbox/sites/app/tmp/02393164-166415211720220926-1793-w6nj56.pdf /Users/●●/Dropbox/sites/app/app/models/send_mask_insurance_file.rb:11:in `block (3 levels) in generate_mask_insurance_zip_file' /Users/●●/Dropbox/sites/app/app/models/send_mask_insurance_file.rb:10:in `block (2 levels) in generate_mask_insurance_zip_file' /Users/●●/Dropbox/sites/app/app/models/send_mask_insurance_file.rb:9:in `block in generate_mask_insurance_zip_file' /Users/●●/Dropbox/sites/app/app/models/send_mask_insurance_file.rb:8:in `generate_mask_insurance_zip_file' /Users/●●/Dropbox/sites/app/lib/tasks/cron.rake:98:in `block (2 levels) in <main>' /Users/●●/Dropbox/sites/app/lib/tasks/cron.rake:7:in `block in task_with_logger_and_notify' /Users/●●/.rbenv/versions/2.7.6/bin/bundle:23:in `load' /Users/●●/.rbenv/versions/2.7.6/bin/bundle:23:in `<main>' Tasks: TOP => cron:generate_mask_insurance_zip_file (See full trace by running task with --trace)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

winterboum

2022/09/25 12:18

エラーメッセージは全文載せてください
ssk

2022/09/25 12:30

ありがとうございます。 エラーメッセージを追加いたしました。
winterboum

2022/09/25 13:48

send_mask_insurance_file.rb:10 って zip_tempfile = Tempfile.new(["#{Time.current.to_i}", ".zip"], Rails.root.join('tmp')) ですか?
ssk

2022/09/25 14:04

10行目は以下になります。 Zip::File.open(zip_tempfile.path, Zip::File::CREATE) do |zipfile|
winterboum

2022/09/25 14:33

とすると、エラーメッセージが出たのは載せてある class SendMaskInsuranceFile とは別物ですね。 8行目が generate_mask_insurance_zip_file 9行目が block in generate_mask_insurance_zip_file だと言ってますから 載せてるfileだとすると 10行目は SendMaskInsuranceFile.where(created_at: になりますが。
ssk

2022/09/25 14:48

申し訳ございません。 余計な改行が入っていました。 正しくは11行目が以下になります。 Zip::File.open(zip_tempfile.path, Zip::File::CREATE) do |zipfile| 内容も修正いたしました。
guest

回答2

0

Tempfile の一時ファイルは自動的に削除されます。
closeでtrueを指定しなかった場合でもGCで削除されます。
https://docs.ruby-lang.org/ja/latest/class/Tempfile.html#I_CLOSE

そのcsvを読むループの中でGCが動いているのかどうかはわかりませんが、
Tempfileの使い方としてはよくない(基本的に close の前に使う)と思います。

投稿2022/09/25 23:04

編集2022/09/25 23:04
sigsegv

総合スコア895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ssk

2022/09/26 00:35

ありがとうございます。 Tempfile.createに変更することでブロックを抜けたタイミングで一時ファイルが削除されるかと思いますが、 Tempfile.createにしてもブロックを抜ける前に一時ファイルが削除されているようでした。 (エラーメッセージにも変化がありましたので、上記の内容を更新いたしました。)
sigsegv

2022/09/26 15:21 編集

zipfile.add()はzipに格納するファイルを登録するだけで、そのファイルをzipに書き出すのは、 Zip::File を close するとき(=openのブロックを抜けるとき)だと思います。 つまり、zipfile.add() の時に一時ファイルが存在しているだけでは不十分で、 Zip::File.open ~ を抜けるときまで一時ファイルが残っていないとだめです。
winterboum

2022/09/27 00:33

初見はそこだったのですが、この場合 「zipfile.add の行でエラー」 とならないのでしょうか? Zip::File.open の loopの中ではありますが。
sigsegv

2022/09/27 04:20

zipfile.add の時点は、ファイル(一時ファイル)が存在するのでエラーになりません。
sigsegv

2022/09/27 04:28

なんか伝わってない気がしますね。 zipfile.add() ではそのファイルが存在するかどうか、通常ファイルかどうかなどをチェックして、 問題なければ zipfileオブジェクト内部のリストにそのパスを追加します。 最後に(zipfileをクローズするとき)にリストにあるパスを順次読み出し、zipファイルを作成します。 この時点でファイルが削除されているとエラーとなります。これが質問のエラーです。 細部は違ってるかもしれませんが、概ね上記のような動きをしているのだろうというのが回答内容です。
guest

0

なかなか不思議な現象です。
10行目で
zip_tempfile = Tempfile.new(["#{Time.current.to_i}", ".zip"], Rails.root.join('tmp'))
と、zip_tempfile の拡張子は zip である、としているのに 11行目の
Zip::File.open(zip_tempfile.path, Zip::File::CREATE) do |zipfile|
で起きたエラーでは
/Users/●●/Dropbox/sites/app/tmp/02393164-166410115520220925-6791-ba6on1.pdf
pdf だと言ってる。
次の行ですから値が変わるはずがないのに。
これを見ると載っている class SendMaskInsuranceFile が吐いたエラーに見えないのですが。

追記
ついでに書いておきますが
この問題が解決し

Zip::File.open(zip_tempfile.path, Zip::File::CREATE) do |zipfile| end send_mask_insurance_file.update!(zip_url: zip_tempfile)

まで無事に終わっても、send_mask_insurance_file.zip_url が示す先にはfileの実体はありませんよ。
Tempfileなので削除されます。

投稿2022/09/25 22:37

編集2022/09/25 22:49
winterboum

総合スコア23329

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ssk

2022/09/26 00:33

ありがとうございます。 コードのTempfile.newをTempfile.createに変更し、ブロックで処理してみましたが。 同じようにpdfを保持?することができませんでした.... (エラーメッセージにも変化がありましたので、上記の内容を更新いたしました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問