класс Tempfile
Утилитарный класс для управления временными файлами. При создании объекта Tempfile, он создаст временный файл с уникальным именем. Объект Tempfile ведет себя как объект File, и вы можете выполнять все обычные файловые операции: чтение данных, запись данных, изменение разрешений и т.д. Хотя этот класс не документирует все методы экземпляров, поддерживаемые File, вы можете фактически вызывать любой метод экземпляра File на объекте Tempfile.
Синопсис
require 'tempfile'
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 собирается сборщиком мусора или когда интерпретатор Ruby завершается, связанный временный файл автоматически удаляется. Это означает, что нет необходимости явно удалять Tempfile после использования, хотя это хорошая практика: неявное удаление неиспользуемых 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, и вам также не нужно знать имя файла Tempfile.
Например, практическое применение unlink-после-создания было бы таким: вам нужен большой буфер байтов, слишком большой для комфортного размещения в ОЗУ, например, когда вы пишете веб-сервер и хотите буферизовать данные загружаемого клиентом файла.
Дополнительную информацию и пример кода см. в unlink.
Небольшие замечания
Метод выбора имени файла Tempfile безопасен как для многопоточности, так и для межпроцессного взаимодействия: он гарантирует, что никакие другие потоки или процессы не выберут одно и то же имя файла.
Однако сам Tempfile может быть не полностью потокобезопасным. Если вы обращаетесь к одному и тому же объекту Tempfile из нескольких потоков, то вы должны защитить его с помощью мьютекса.
Константы
- VERSION
Открытые методы класса
# File lib/tempfile.rb, line 438
def Tempfile.create(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, основанный на этом файле.
Без блока и без аргументов создаёт и возвращает файл с:
-
Директорией — временной директорией системы (зависит от системы).
-
Уникальным именем файла в этой директории.
-
Разрешениями —
0600; см. Разрешения файлов. -
Режимом —
'w+'(режим чтения/записи, позиция в конце).
Без блока файл не удаляется автоматически, поэтому его нужно удалить явно.
Пример:
f = Tempfile.create # => #<File:/tmp/20220505-9795-17ky6f6> f.class # => File f.path # => "/tmp/20220505-9795-17ky6f6" f.stat.mode.to_s(8) # => "100600" File.exist?(f.path) # => true File.unlink(f.path) File.exist?(f.path) # => false
Аргумент 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, см. Параметры открытия.
С заданным блоком, создаёт файл как выше, передаёт его в блок, возвращает значение блока; перед возвратом объект файла закрывается, а базовый файл удаляется:
Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"
Связанное: Tempfile.new.
# File lib/tempfile.rb, line 150
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
@finalizer_obj = Object.new
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
ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path))
ObjectSpace.define_finalizer(self, Closer.new(tmpfile))
super(tmpfile)
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.
Защищённые методы класса
# File lib/tempfile.rb, line 366
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 208 def close(unlink_now=false) _close unlink if unlink_now end
Закрывает файл. Если unlink_now равно true, то файл будет удалён (удалён) после закрытия. Конечно, вы можете впоследствии вызвать unlink, если вы не удаляете его сейчас.
Если вы не удаляете временный файл явно, удаление будет отложено до завершения объекта.
# File lib/tempfile.rb, line 215 def close! close(true) end
Закрывает и удаляет (удаляет) файл. Имеет тот же эффект, что и вызов close(true).
# File lib/tempfile.rb, line 174 def initialize_clone(other) initialize_copy_iv(other) super(other) ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end
# File lib/tempfile.rb, line 168 def initialize_dup(other) initialize_copy_iv(other) super(other) ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end
# File lib/tempfile.rb, line 188 def open _close ObjectSpace.undefine_finalizer(self) mode = @mode & ~(File::CREAT|File::EXCL) __setobj__(File.open(__getobj__.path, mode, **@opts)) ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) __getobj__ end
Открывает или повторно открывает файл в режиме «r+».
# File lib/tempfile.rb, line 268 def path @unlinked ? nil : __getobj__.path end
Возвращает полное имя пути временного файла. Это будет пусто, если unlink был вызван.
# File lib/tempfile.rb, line 274
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 252
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
ObjectSpace.undefine_finalizer(@finalizer_obj)
@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
Закрытые методы экземпляра
# File lib/tempfile.rb, line 180
def initialize_copy_iv(other)
@unlinked = other.unlinked
@mode = other.mode
@opts = other.opts
@finalizer_obj = other.finalizer_obj
end
Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.