Spec-Zone.ru › Ruby 3.4

класс Tempfile

Утилитарный класс для управления временными файлами.

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

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

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

Tempfile.create создаёт обычный объект File. Время удаления файла предсказуемо. Также он поддерживает технику open-and-unlink, которая удаляет временный файл сразу после создания.

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, вы можете фактически вызвать любой метод экземпляра File на объекте Tempfile.

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

  • Длительные интервалы GC и консервативные GC могут накапливать временные файлы, которые не удаляются.

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

Существуют устаревшие рекомендации для Tempfile.new и Tempfile.open.

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

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

Поэтому всегда следует вызывать unlink или close в блоке 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, и вам также не нужно знать имя файла Tempfile.

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

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

‘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, основанный на этом файле.

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

  • Class является 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. O_TMPFILE используется в Linux.

Связанные методы: 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).

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

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

  • Class является 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

Возвращает полное имя пути временного файла. Будет равно null, если unlink был вызван.

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–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

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