Creating a thread-safe temporary file name

后端 未结 4 723
既然无缘
既然无缘 2020-12-29 21:18

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 ther

相关标签:
4条回答
  • 2020-12-29 21:32

    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"
    
    0 讨论(0)
  • 2020-12-29 21:34

    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
    
    0 讨论(0)
  • 2020-12-29 21:39

    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

    0 讨论(0)
  • 2020-12-29 21:41

    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
    
    0 讨论(0)
提交回复
热议问题