Creating a thread-safe temporary file name

ε祈祈猫儿з 提交于 2019-12-20 17:38:59

问题


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

回答1:


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"



回答2:


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



回答3:


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




回答4:


I found the Dir:Tmpname solution did not work for me. When evaluating this:

Dir::Tmpname.make_tmpname "/tmp/blob", nil

Under MRI Ruby 1.9.3p194 I get:

uninitialized constant Dir::Tmpname (NameError)

Under JRuby 1.7.5 (1.9.3p393) I get:

NameError: uninitialized constant Dir::Tmpname

You might try something like this:

def temp_name(file_name='', ext='', dir=nil)
    id   = Thread.current.hash * Time.now.to_i % 2**32
    name = "%s%d.%s" % [file_name, id, ext]
    dir ? File.join(dir, name) : name
end


来源:https://stackoverflow.com/questions/13787746/creating-a-thread-safe-temporary-file-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!