How to write a Tempfile as binary

Viewed 6283

When trying to write a string / unzipped file to a Tempfile by doing:

temp_file = Tempfile.new([name, extension])
temp_file.write(unzipped_io.read)

Which throws the following error when I do this with an image:

Encoding::UndefinedConversionError - "\xFF" from ASCII-8BIT to UTF-8

When researching it I found out that this is caused because Ruby tries to write files with an encoding by default (UTF-8). But the file should be written as binary, so it ignores any file specific behavior.

Writing regular File you would be able to do this as following:

File.open('/tmp/test.jpg', 'rb') do |file| 
  file.write(unzipped_io.read)
end

How to do this in Tempfile

3 Answers

Tempfile.new passes options to File.open which accepts the options from IO.new, in particular:

:binmode
If the value is truth value, same as “b” in argument mode.

So to open a tempfile in binary mode, you'd use:

temp_file = Tempfile.new([name, extension], binmode: true)

temp_file.binmode?          #=> true
temp_file.external_encoding #=> #<Encoding:ASCII-8BIT>

In addition, you might want to use Tempfile.create which takes a block and automatically closes and removes the file afterwards:

Tempfile.create([name, extension], binmode: true) do |temp_file|
  temp_file.write(unzipped_io.read)
  # ...
end

I have encountered the solution in an old Ruby forum post, so I thought I would share it here, making it easier for people to find:

https://www.ruby-forum.com/t/ruby-binary-temp-file/116791

Apparently Tempfile has an undocumented method binmode, which changes the writing mode to binary and thus ignoring any encoding issues:

temp_file = Tempfile.new([name, extension])
temp_file.binmode
temp_file.write(unzipped_io.read)

Thanks unknown person who mentioned it on ruby-forums.com in 2007!

Another alternative is IO.binwrite(path, file_content)

Related