Creating a thread-safe temporary file name Creating a thread-safe temporary file name ruby ruby

Creating a thread-safe temporary file name


Dir::Tmpname.create

You could use Dir::Tmpname.create. It figures out what temporary directory to use (unless you pass it a directory). It's a little ugly to use given that it expects a block:

require 'tmpdir'# => trueDir::Tmpname.create(['prefix-', '.ext']) {}# => "/tmp/prefix-20190827-1-87n9iu.ext"Dir::Tmpname.create(['prefix-', '.ext'], '/my/custom/directory') {}# => "/my/custom/directory/prefix-20190827-1-11x2u0h.ext"

The block is there for code to test if the file exists and raise an Errno::EEXIST so that a new name can be generated with incrementing value appended on the end.

The Rails Solution

The solution implemented by Ruby on Rails is short and similar to the solution originally implemented in Ruby:

require 'tmpdir'# => trueFile.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX")=> "/tmp/YOUR_PREFIX-20190827-1-wyouwg-YOUR_SUFFIX"File.join(Dir.tmpdir, "YOUR_PREFIX-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-YOUR_SUFFIX")=> "/tmp/YOUR_PREFIX-20190827-1-140far-YOUR_SUFFIX"

Dir::Tmpname.make_tmpname (Ruby 2.5.0 and earlier)

Dir::Tmpname.make_tmpname was removed in Ruby 2.5.0. Prior to Ruby 2.4.4 it could accept a directory path as a prefix, but as of Ruby 2.4.4, directory separators are removed.

Digging in tempfile.rb you'll notice that Tempfile includes Dir::Tmpname. Inside you'll find make_tmpname which does what you ask for.

require 'tmpdir'# => trueFile.join(Dir.tmpdir, Dir::Tmpname.make_tmpname("prefix-", nil))# => "/tmp/prefix-20190827-1-dfhvld"File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], nil))# => "/tmp/prefix-20190827-1-19zjck1.ext"File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".ext"], "suffix"))# => "/tmp/prefix-20190827-1-f5ipo7-suffix.ext"


Since Dir::Tmpname.make_tmpname was removed in Ruby 2.5.0, this one falls back to using SecureRandom:

require "tmpdir"def generate_temp_filename(ext=".png")  filename = begin    Dir::Tmpname.make_tmpname(["x", ext], nil)  rescue NoMethodError    require "securerandom"    "#{SecureRandom.urlsafe_base64}#{ext}"  end  File.join(Dir.tmpdir, filename)end


Since you only need the filename, what about using the SecureRandom for that:

require 'securerandom'filename =  "#{SecureRandom.hex(6)}.png" #=> "0f04dd94addf.png"

You can also use SecureRandom.alphanumeric