In this piece of Ruby code that processes an UploadedFile
using exifr
f = uploaded_file.tempfilep "1 #{f.closed?} #{f.instance_variable_get(:'@unlinked')}"#1 EXIFR::JPEG.new(StringIO.new(f.read))#2 EXIFR::JPEG.new(f)p "2 #{f.closed?} #{f.instance_variable_get(:'@unlinked')}"GC.startsleep 0.01p "3 #{f.closed?} #{f.instance_variable_get(:'@unlinked')}"p "4 #{f.size}"
N.B. GC.start/sleep is there to make the problem replicate reliably.
when uncommenting #1
, all is fine:
"1 false false""2 false false""3 false false""4 3822528"
However, the outcome of uncommenting #2
, instead of #1
, yields this:
"1 false false""2 false false""3 true false"[c4b7ce6b-5492-43db-8c64-726cafaccce0] [Thread: 24800] Errno::ENOENT (No such file or directory @ rb_file_s_size - /var/folders/vx/v0rn818s0257_3l491_v48bm0000gn/T/RackMultipart20240221-71765-acbi7v.JPG):
Now all that exifr is doing is this:
def initialize(file, load_thumbnails: true)... examine(file.dup, load_thumbnails: load_thumbnails)... end end class Reader < SimpleDelegator def readbyte; readchar; end unless File.method_defined?(:readbyte) def readint; (readbyte << 8) + readbyte; end def readframe; read(readint - 2); end def readsof; [readint, readbyte, readint, readint, readbyte]; end def next c = readbyte while c != 0xFF c = readbyte while c == 0xFF c end end def examine(io, load_thumbnails: true) io = Reader.new(io)...
and a bit of reading from io
, so I don't understand what would cause the file to get closed.
This happens in a Rails app running on puma.
#2 would be preferable, as it does not require the file to be loaded into memory completely (in my case, we are talking up to 50 MB).