Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a thread-safe temporary file name

When using Tempfile Ruby is creating a file with a thread-safe and inter-process-safe name. I only need a file name in that way.

I was wondering if there is a more straight forward approach way than:

t = Tempfile.new(['fleischwurst', '.png'])
temp_path = t.path
t.close
t.unlink
like image 472
iltempo Avatar asked Dec 09 '12 13:12

iltempo


People also ask

What is the name of temporary file?

Alternatively referred to as a foo file, a temporary file or temp file is a file created to hold information while a file's being created or modified. After the program is closed, the temporary file is deleted.

What is an example of a temporary file?

Techopedia Explains Temporary File In terms of backup purposes, Microsoft's Office applications are good examples of this. For example, Microsoft Word and Excel save a temporary file associated with the current open document that it points to after a computer has recovered from a crash or power outage.

How do I create a temp file?

To create and use a temporary fileThe application opens the user-provided source text file by using CreateFile. The application retrieves a temporary file path and file name by using the GetTempPath and GetTempFileName functions, and then uses CreateFile to create the temporary file.

How do I create a temporary file in CPP?

C++ tmpfile()The tmpfile() function in C++ creates and opens a temporary file in binary read/write (wb+) mode with a unique auto-generated filename. The file will be automatically deleted when it is closed by the program(by executing fclose) or when the program terminates.


3 Answers

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' # => true Dir::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' # => true File.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' # => true File.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" 
like image 134
Jan Avatar answered Sep 22 '22 01:09

Jan


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
like image 26
Abe Voelker Avatar answered Sep 24 '22 01:09

Abe Voelker


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

like image 42
Paulo Fidalgo Avatar answered Sep 24 '22 01:09

Paulo Fidalgo