Spec-Zone.ru › Ruby 3.4

class Tempfile

Вспомогательный класс для управления временными файлами.

Существует два способа создания временного файла:

  • Tempfile.create (рекомендуется)

  • Tempfile.new и Tempfile.open (в основном для обратной совместимости, не рекомендуются)

Tempfile.create создает обычный объект File. Время удаления файла предсказуемо. Кроме того, поддерживается техника открытия с последующим удалением ссылки на файл, при которой временный файл удаляется сразу после создания.

Tempfile.new и Tempfile.open создают объект Tempfile. Созданный файл удаляется сборщиком мусора GC (финализатором). Время удаления файла непредсказуемо.

Краткое описание

require 'tempfile'

# Tempfile.create with a block
# The filename are choosen automatically.
# (You can specify the prefix and suffix of the filename by an optional argument.)
Tempfile.create {|f|
  f.puts "foo"
  f.rewind
  f.read                # => "foo\n"
}                       # The file is removed at block exit.

# Tempfile.create without a block
# You need to unlink the file in non-block form.
f = Tempfile.create
f.puts "foo"
f.close
File.unlink(f.path)     # You need to unlink the file.

# Tempfile.create(anonymous: true) without a block
f = Tempfile.create(anonymous: true)
# The file is already removed because anonymous.
f.path                  # => "/tmp/"  (no filename since no file)
f.puts "foo"
f.rewind
f.read                  # => "foo\n"
f.close

# Tempfile.create(anonymous: true) with a block
Tempfile.create(anonymous: true) {|f|
  # The file is already removed because anonymous.
  f.path                # => "/tmp/"  (no filename since no file)
  f.puts "foo"
  f.rewind
  f.read                # => "foo\n"
}

# Not recommended: Tempfile.new without a block
file = Tempfile.new('foo')
file.path      # => A unique filename in the OS's temp directory,
               #    e.g.: "/tmp/foo.24722.0"
               #    This filename contains 'foo' in its basename.
file.write("hello world")
file.rewind
file.read      # => "hello world"
file.close
file.unlink    # deletes the temp file

О Tempfile.new и Tempfile.open

Этот раздел не относится к Tempfile.create, поскольку он возвращает объект File (а не объект Tempfile).

При создании объекта Tempfile будет создан временный файл с уникальным именем. Объект Tempfile ведет себя так же, как объект File, и с ним можно выполнять все обычные файловые операции: читать и записывать данные, изменять разрешения и т. д. Поэтому, хотя в этом классе явно не документированы все методы экземпляра, поддерживаемые File, на самом деле для объекта Tempfile можно вызывать любой метод экземпляра File.

У объекта Tempfile есть финализатор для удаления временного файла. Это означает, что временный файл удаляется посредством GC. Это может привести к нескольким проблемам:

  • Большие интервалы между запусками GC и консервативная работа GC могут привести к накоплению неудаленных временных файлов.

  • Временные файлы не удаляются, если Ruby завершается аварийно (например, из-за SIGKILL или SEGV).

Для Tempfile.new и Tempfile.open существуют следующие рекомендации, принятые для обратной совместимости.

Явное закрытие

Когда объект Tempfile собирается сборщиком мусора или завершается интерпретатор Ruby, связанный с ним временный файл удаляется автоматически. Это значит, что после использования Tempfile не обязательно удалять его явно, хотя делать это рекомендуется: если явно не удалять неиспользуемые временные файлы, в файловой системе может остаться их большое количество, пока они не будут собраны сборщиком мусора. Наличие этих временных файлов может затруднить подбор нового имени файла Tempfile.

Поэтому всегда следует вызывать unlink или закрывать файл в блоке ensure, например:

file = Tempfile.new('foo')
begin
   # ...do something with file...
ensure
   file.close
   file.unlink   # deletes the temp file
end

Для этой цели существует Tempfile.create { … }, которым удобнее пользоваться. Обратите внимание, что Tempfile.create возвращает экземпляр File, а не Tempfile, что также позволяет избежать накладных расходов и сложностей, связанных с делегированием.

Tempfile.create('foo') do |file|
   # ...do something with file...
end

Удаление ссылки после создания

В POSIX-системах файл можно удалить сразу после создания, до его закрытия. При этом запись файла в файловой системе удаляется, но файловый дескриптор остается открытым, поэтому доступ к содержимому файла сохраняется только у процессов, которые уже открыли его. Настоятельно рекомендуется делать это, если вы не хотите, чтобы другие процессы могли читать из Tempfile или записывать в него, и вам также не нужно знать имя временного файла.

Кроме того, это гарантирует удаление временного файла даже при аварийном завершении Ruby. ОС освобождает место, занимаемое временным файлом, когда файл закрывается или процесс Ruby завершается (нормально или аварийно).

Например, практический сценарий использования удаления ссылки сразу после создания: вам нужен большой буфер байтов, который слишком велик, чтобы удобно хранить его в оперативной памяти. Например, при написании веб-сервера, которому нужно буферизовать данные загружаемого клиентом файла.

‘Tempfile.create(anonymous: true)` поддерживает такое поведение. Оно также работает в Windows.

Примечания

Метод Tempfile для выбора имени файла безопасен как для потоков, так и для процессов: он гарантирует, что другие потоки или процессы не выберут то же имя.

Однако сам Tempfile может быть не полностью безопасен для потоков. Если вы обращаетесь к одному объекту Tempfile из нескольких потоков, следует защитить его мьютексом.

Константы

VERSION

Версия

Общедоступные методы класса

create (basename="", tmpdir=nil, mode: 0, anonymous: false, **options, &block) Показать исходный код
# File lib/tempfile.rb, line 558
def Tempfile.create(basename="", tmpdir=nil, mode: 0, anonymous: false, **options, &block)
  if anonymous
    create_anonymous(basename, tmpdir, mode: mode, **options, &block)
  else
    create_with_filename(basename, tmpdir, mode: mode, **options, &block)
  end
end

Создает файл в базовой файловой системе и возвращает новый объект File, основанный на этом файле.

Если блок и аргументы не указаны, создает и возвращает файл, у которого:

  • Класс — File (не Tempfile).

  • Каталог — системный временный каталог (зависит от системы).

  • Сгенерированное имя файла уникально в этом каталоге.

  • Разрешения — 0600; см. раздел Разрешения файлов.

  • Режим — 'w+' (чтение/запись, позиция в конце файла).

Способ удаления временного файла зависит от именованного аргумента anonymous и от того, указан ли блок. Подробнее см. описание именованного аргумента anonymous далее.

Пример:

f = Tempfile.create     # => #<File:/tmp/20220505-9795-17ky6f6>
f.class                 # => File
f.path                  # => "/tmp/20220505-9795-17ky6f6"
f.stat.mode.to_s(8)     # => "100600"
f.close
File.exist?(f.path)     # => true
File.unlink(f.path)
File.exist?(f.path)     # => false

Tempfile.create {|f|
  f.puts "foo"
  f.rewind
  f.read                # => "foo\n"
  f.path                # => "/tmp/20240524-380207-oma0ny"
  File.exist?(f.path)   # => true
}                       # The file is removed at block exit.

f = Tempfile.create(anonymous: true)
# The file is already removed because anonymous
f.path                  # => "/tmp/"  (no filename since no file)
f.puts "foo"
f.rewind
f.read                  # => "foo\n"
f.close

Tempfile.create(anonymous: true) {|f|
  # The file is already removed because anonymous
  f.path                # => "/tmp/"  (no filename since no file)
  f.puts "foo"
  f.rewind
  f.read                # => "foo\n"
}

Аргумент basename, если указан, может быть одним из следующих:

  • Строка: сгенерированное имя файла начинается с basename:

    Tempfile.create('foo') # => #<File:/tmp/foo20220505-9795-1gok8l9>
    
  • Массив из двух строк [prefix, suffix]: сгенерированное имя файла начинается с prefix и заканчивается на suffix:

    Tempfile.create(%w/foo .jpg/) # => #<File:/tmp/foo20220505-17839-tnjchh.jpg>
    

При указании аргументов basename и tmpdir файл создается в каталоге tmpdir:

Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>

Именованные аргументы mode и options напрямую передаются методу File.open:

  • Значение, заданное для mode, должно быть целым числом и может быть выражено как логическое ИЛИ констант, определенных в File::Constants.

  • Описание options см. в разделе Параметры открытия.

Именованный аргумент anonymous задает момент удаления файла.

  • anonymous=false (по умолчанию) без блока: файл не удаляется.

  • anonymous=false (по умолчанию) с блоком: файл удаляется после завершения блока.

  • anonymous=true без блока: файл удаляется перед возвратом.

  • anonymous=true с блоком: файл удаляется до вызова блока.

В первом случае (anonymous=false без блока) файл не удаляется автоматически. Его необходимо явно закрыть. Его можно использовать, чтобы переименовать файл в нужное имя. Если файл больше не нужен, его следует явно удалить.

Метод File#path созданного объекта файла возвращает временный каталог с завершающей косой чертой, если anonymous имеет значение true.

Если указан блок, файл создается описанным выше способом, передается блоку, а возвращаемым значением становится результат блока. Перед возвратом объект файла закрывается, а базовый файл удаляется:

Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"

Примечание по реализации:

Именованный аргумент +anonymous=true+ реализован с помощью FILE_SHARE_DELETE в Windows. В Linux используется O_TMPFILE.

Связанный метод: Tempfile.new.

new (basename="", tmpdir=nil, mode: 0, **options) Показать исходный код
# File lib/tempfile.rb, line 219
def initialize(basename="", tmpdir=nil, mode: 0, **options)
  warn "Tempfile.new doesn't call the given block.", uplevel: 1 if block_given?

  @unlinked = false
  @mode = mode|File::RDWR|File::CREAT|File::EXCL
  tmpfile = nil
  ::Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
    opts[:perm] = 0600
    tmpfile = File.open(tmpname, @mode, **opts)
    @opts = opts.freeze
  end

  super(tmpfile)

  @finalizer_manager = FinalizerManager.new(__getobj__.path)
  @finalizer_manager.register(self, __getobj__)
end

Создает файл в базовой файловой системе и возвращает новый объект Tempfile, основанный на этом файле.

По возможности рассмотрите использование Tempfile.create, которое:

  • Позволяет избежать снижения производительности из-за делегирования, возникающего, когда Tempfile.new вызывает метод суперкласса DelegateClass(File).

  • Не полагается на финализатор для закрытия файла и удаления ссылки на него, поскольку это может работать ненадежно.

Создает и возвращает файл, у которого:

  • Класс — Tempfile (не File, как в случае с Tempfile.create).

  • Каталог — системный временный каталог (зависит от системы).

  • Сгенерированное имя файла уникально в этом каталоге.

  • Разрешения — 0600; см. раздел Разрешения файлов.

  • Режим — 'w+' (чтение/запись, позиция в конце файла).

Базовый файл удаляется, когда объект Tempfile уничтожается и освобождается сборщиком мусора.

Пример:

f = Tempfile.new # => #<Tempfile:/tmp/20220505-17839-1s0kt30>
f.class               # => Tempfile
f.path                # => "/tmp/20220505-17839-1s0kt30"
f.stat.mode.to_s(8)   # => "100600"
File.exist?(f.path)   # => true
File.unlink(f.path)   #
File.exist?(f.path)   # => false

Аргумент basename, если указан, может быть одним из следующих:

  • Строка: сгенерированное имя файла начинается с basename:

    Tempfile.new('foo') # => #<Tempfile:/tmp/foo20220505-17839-1whk2f>
    
  • Массив из двух строк [prefix, suffix]: сгенерированное имя файла начинается с prefix и заканчивается на suffix:

    Tempfile.new(%w/foo .jpg/) # => #<Tempfile:/tmp/foo20220505-17839-58xtfi.jpg>
    

При указании аргументов basename и tmpdir файл создается в каталоге tmpdir:

Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>

Именованные аргументы mode и options напрямую передаются методу File.open:

  • Значение, заданное с помощью mode, должно быть целым числом и может быть выражено как логическое ИЛИ констант, определенных в File::Constants.

  • Описание options см. в разделе Параметры открытия.

Связанный метод: Tempfile.create.

Вызывает метод суперкласса
open (*args, **kw) { |tempfile| ... } Показать исходный код
# File lib/tempfile.rb, line 439
def open(*args, **kw)
  tempfile = new(*args, **kw)

  if block_given?
    begin
      yield(tempfile)
    ensure
      tempfile.close
    end
  else
    tempfile
  end
end

Создает новый объект Tempfile.

Этот метод не рекомендуется и существует в основном для обратной совместимости. Вместо него используйте Tempfile.create, который позволяет избежать затрат на делегирование, не полагается на финализатор и удаляет ссылку на файл при указании блока.

Tempfile.open по-прежнему подходит, если нужно, чтобы ссылка на Tempfile удалялась финализатором, и невозможно явно определить, в какой части программы ссылку на Tempfile можно безопасно удалить.

Если блок не указан, этот метод является синонимом Tempfile.new.

Если блок указан, будет создан объект Tempfile, а блок будет выполнен с объектом Tempfile в качестве аргумента. Объект Tempfile автоматически закроется после завершения блока. Однако файл не будет удален, и ссылку на него нужно удалить вручную с помощью Tempfile#close! или Tempfile#unlink. Финализатор попытается удалить ссылку на файл, но полагаться на это не следует: файл может оставаться на диске гораздо дольше, чем предполагалось. Например, в CRuby выполнение финализаторов может задерживаться из-за консервативного сканирования стека и ссылок, оставшихся в неиспользуемой памяти.

Вызов возвращает значение блока.

В любом случае все аргументы (*args) будут переданы в Tempfile.new.

Tempfile.open('foo', '/home/temp') do |f|
   # ... do something with f ...
end

# Equivalent:
f = Tempfile.open('foo', '/home/temp')
begin
   # ... do something with f ...
ensure
   f.close
end

Закрытые методы класса

create_anonymous (basename="", tmpdir=nil, mode: 0, **options) { |tmpfile| ... } Показать исходный код
# File lib/tempfile.rb, line 606
        def create_anonymous(basename="", tmpdir=nil, mode: 0, **options, &block)
  tmpfile = nil
  tmpdir = Dir.tmpdir() if tmpdir.nil?
  if defined?(File::TMPFILE) # O_TMPFILE since Linux 3.11
    begin
      tmpfile = File.open(tmpdir, File::RDWR | File::TMPFILE, 0600)
    rescue Errno::EISDIR, Errno::ENOENT, Errno::EOPNOTSUPP
      # kernel or the filesystem does not support O_TMPFILE
      # fallback to create-and-unlink
    end
  end
  if tmpfile.nil?
    mode |= File::SHARE_DELETE | File::BINARY # Windows needs them to unlink the opened file.
    tmpfile = create_with_filename(basename, tmpdir, mode: mode, **options)
    File.unlink(tmpfile.path)
    tmppath = tmpfile.path
  end
  path = File.join(tmpdir, '')
  unless tmppath == path
    # clear path.
    tmpfile.autoclose = false
    tmpfile = File.new(tmpfile.fileno, mode: File::RDWR, path: path)
    PathAttr.set_path(tmpfile, path) if defined?(PathAttr)
  end
  if block
    begin
      yield tmpfile
    ensure
      tmpfile.close
    end
  else
    tmpfile
  end
end
create_with_filename (basename="", tmpdir=nil, mode: 0, **options) { |tmpfile| ... } Показать исходный код
# File lib/tempfile.rb, line 567
        def create_with_filename(basename="", tmpdir=nil, mode: 0, **options)
  tmpfile = nil
  Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
    mode |= File::RDWR|File::CREAT|File::EXCL
    opts[:perm] = 0600
    tmpfile = File.open(tmpname, mode, **opts)
  end
  if block_given?
    begin
      yield tmpfile
    ensure
      unless tmpfile.closed?
        if File.identical?(tmpfile, tmpfile.path)
          unlinked = File.unlink tmpfile.path rescue nil
        end
        tmpfile.close
      end
      unless unlinked
        begin
          File.unlink tmpfile.path
        rescue Errno::ENOENT
        end
      end
    end
  else
    tmpfile
  end
end

Общедоступные методы экземпляра

close (unlink_now=false) Показать исходный код
# File lib/tempfile.rb, line 279
def close(unlink_now=false)
  _close
  unlink if unlink_now
end

Закрывает файл. Если unlink_now имеет значение true, после закрытия файл будет удален. Разумеется, если вы не удалили ссылку на него сейчас, позже можно вызвать unlink.

Если явно не удалить ссылку на временный файл, его удаление будет отложено до финализации объекта.

close! () Показать исходный код
# File lib/tempfile.rb, line 286
def close!
  close(true)
end

Закрывает файл и удаляет ссылку на него (удаляет файл). Имеет тот же эффект, что и вызов close(true).

delete ()
Псевдоним для: unlink
length ()
Псевдоним для: size
open () Показать исходный код
# File lib/tempfile.rb, line 257
def open
  _close

  mode = @mode & ~(File::CREAT|File::EXCL)
  __setobj__(File.open(__getobj__.path, mode, **@opts))

  @finalizer_manager.register(self, __getobj__)

  __getobj__
end

Открывает или повторно открывает файл в режиме «r+».

path () Показать исходный код
# File lib/tempfile.rb, line 341
def path
  @unlinked ? nil : __getobj__.path
end

Возвращает полный путь к временному файлу. Если был вызван unlink, возвращает nil.

size () Показать исходный код
# File lib/tempfile.rb, line 347
def size
  if !__getobj__.closed?
    __getobj__.size # File#size calls rb_io_flush_raw()
  else
    File.size(__getobj__.path)
  end
end

Возвращает размер временного файла. При этом буфер IO сбрасывается перед определением размера.

Также имеет псевдоним: length
unlink () Показать исходный код
# File lib/tempfile.rb, line 323
def unlink
  return if @unlinked
  begin
    File.unlink(__getobj__.path)
  rescue Errno::ENOENT
  rescue Errno::EACCES
    # may not be able to unlink on Windows; just ignore
    return
  end

  @finalizer_manager.unlinked = true

  @unlinked = true
end

Удаляет файл из файловой системы. После использования файл всегда следует удалять, как описано в разделе рекомендаций «Явное закрытие» в обзоре Tempfile:

file = Tempfile.new('foo')
begin
   # ...do something with file...
ensure
   file.close
   file.unlink   # deletes the temp file
end

Удаление ссылки до закрытия

В POSIX-системах можно удалить ссылку на файл до его закрытия. Эта практика подробно описана в обзоре Tempfile (раздел «Удаление ссылки после создания»); дополнительные сведения см. там.

Однако удаление ссылки до закрытия может не поддерживаться операционными системами, отличными от POSIX. Наиболее известный пример — Microsoft Windows: удаление ссылки на незакрытый файл приводит к ошибке, которую этот метод молча игнорирует. Если вы хотите по возможности удалять ссылку до закрытия, пишите код следующим образом:

file = Tempfile.new('foo')
file.unlink   # On Windows this silently fails.
begin
   # ... do something with file ...
ensure
   file.close!   # Closes the file handle. If the file wasn't unlinked
                 # because #unlink failed, then this method will attempt
                 # to do so again.
end
Также имеет псевдоним: delete

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API