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
-
Версия
Общедоступные методы класса
# 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.
# 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.
# 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
Закрытые методы класса
# 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 # 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 Общедоступные методы экземпляра
# File lib/tempfile.rb, line 279 def close(unlink_now=false) _close unlink if unlink_now end
Закрывает файл. Если unlink_now имеет значение true, после закрытия файл будет удален. Разумеется, если вы не удалили ссылку на него сейчас, позже можно вызвать unlink.
Если явно не удалить ссылку на временный файл, его удаление будет отложено до финализации объекта.
# File lib/tempfile.rb, line 286 def close! close(true) end
Закрывает файл и удаляет ссылку на него (удаляет файл). Имеет тот же эффект, что и вызов close(true).
# 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+».
# File lib/tempfile.rb, line 341 def path @unlinked ? nil : __getobj__.path end
Возвращает полный путь к временному файлу. Если был вызван unlink, возвращает nil.
# 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 сбрасывается перед определением размера.
# 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
Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.