How to write a Tempfile as binary
Asked Answered
E

3

13

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

Eda answered 24/3, 2020 at 11:41 Comment(0)
T
25

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
Taster answered 24/3, 2020 at 13:39 Comment(0)
E
4

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!

Eda answered 24/3, 2020 at 11:41 Comment(3)
From the docs: "[...] you can in fact call any File instance method on a Tempfile object"Taster
From the docs for File in the Methods list, there is no result searching for binmode.Eda
I see my issue now. I looked only 1 depth, expecting to find my method in File, but I should have looked into the next depth where File inherits IOEda
L
3

Another alternative is IO.binwrite(path, file_content)

Liebfraumilch answered 24/3, 2020 at 13:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.