Создание потокобезопасного временного файла с именем

При использовании Tempfile Ruby создает файл с безопасным для потоков и между процессами именем. Мне нужно только имя файла таким образом.

Мне было интересно, есть ли более простой подход, чем:

t = Tempfile.new(['fleischwurst', '.png'])
temp_path = t.path
t.close
t.unlink

person iltempo    schedule 09.12.2012    source источник


Ответы (4)


Dir :: Tmpname.create

Вы можете использовать Dir::Tmpname.create. Он определяет, какой временный каталог использовать (если вы не передадите ему каталог). Это немного некрасиво использовать, учитывая, что он ожидает блок:

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"

Блок предназначен для кода, чтобы проверить, существует ли файл, и поднять Errno::EEXIST, чтобы можно было сгенерировать новое имя с добавлением увеличивающегося значения в конце.

Решение Rails

Решение, реализованное Ruby on Rails, короткое и похоже на решение, изначально реализованное в Рубин:

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 и ранее)

Dir :: Tmpname.make_tmpname был удален в Ruby 2.5.0. До Ruby 2.4.4 он мог принимать путь к каталогу в качестве префикса, но с Ruby 2.4.4 разделители каталогов удалены.

Покопавшись в tempfile.rb, вы заметите, что Tempfile включает Dir::Tmpname. Внутри вы найдете make_tmpname, который выполняет то, о чем вы просите.

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"
person Jan    schedule 09.12.2012
comment
Спасибо, вот и все. Может использоваться с аргументом массива, а также для сохранения расширения имени файла: Dir::Tmpname.make_tmpname(['a', '.png'], nil) - person iltempo; 09.12.2012
comment
@iltempo, пожалуйста. Я добавил ваш пример к ответу. - person Jan; 09.12.2012
comment
Это здорово, но никто не упомянул, что вам нужно require 'tmpdir', чтобы это работало. - person KingBob; 10.08.2016
comment
Dir::Tmpname.make_tmpname был удален в Ruby 2.5 - person Abe Voelker; 27.05.2019
comment
Начиная с Ruby 2.4.4 эта функция больше не работает, поскольку make_tmpname больше не принимает каталог, а только префикс имени файла (символы разделителя каталогов удаляются) - person Brandon Wamboldt; 27.08.2019
comment
это только у меня, или рубиновых документов для встроенного Dir::Tmpname.create нет? Я не могу найти! - person jrochkind; 05.05.2020
comment
Tempfile.new уже использует Dir :: Tmpname.create под капотом, поэтому я не уверен, насколько явное использование Dir :: Tmpname.create дает что-то другое. В документации Ruby предлагается использовать мьютекс для защиты временного файла от нескольких потоков. Кроме того, @jrochkind - это исходный код для этого метода nofollow. com / ruby ​​/ ruby ​​/ blob / - person chemturion; 31.03.2021

Поскольку Dir::Tmpname.make_tmpname был удален в Ruby 2.5.0 , этот возвращается к использованию 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
person Abe Voelker    schedule 27.05.2019

Поскольку вам нужно только имя файла, как насчет использования SecureRandom для этого:

require 'securerandom'

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

Вы также можете использовать SecureRandom.alphanumeric

person Paulo Fidalgo    schedule 16.07.2018

Я обнаружил, что решение Dir: Tmpname не работает для меня. При оценке этого:

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

Под МРТ Ruby 1.9.3p194 я получаю:

uninitialized constant Dir::Tmpname (NameError)

Под JRuby 1.7.5 (1.9.3p393) я получаю:

NameError: uninitialized constant Dir::Tmpname

Вы можете попробовать что-то вроде этого:

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
person dinman2022    schedule 25.02.2014
comment
Перед использованием Dir :: Tempname id вам потребуется 'tempfile' require 'tempfile' Dir :: Tmpname.make_tmpname / tmp / blob, nil Если вы не загрузили 'Tempfile', вы не сможете использовать его расширения для Dir - person Scott Thompson; 07.03.2014