класс 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.open('foo') do |file|
# ...do something with file...
end
Удаление после создания
В системах POSIX возможно удалить файл сразу после его создания и до закрытия. Это удаляет запись в файловой системе без закрытия дескриптора файла, что гарантирует, что только процессы, которые уже открыли дескриптор файла, могут получить доступ к содержимому файла. Настоятельно рекомендуется сделать это, если вы не хотите, чтобы другие процессы могли читать или записывать в Tempfile, и вам также не нужно знать имя файла Tempfile.
Например, практическое применение удаления после создания — это необходимость в большом буфере байтов, который слишком велик для удобного размещения в ОЗУ, например, при написании веб-сервера, когда вы хотите буферизировать данные загружаемого клиентом файла.
Дополнительную информацию и пример кода см. в unlink.
Дополнительные замечания
Метод выбора имени файла Tempfile является потокобезопасным и безопасным для межпроцессного взаимодействия: он гарантирует, что другие потоки или процессы не выберут одно и то же имя файла.
Сам Tempfile однако может быть не полностью потокобезопасным. Если вы обращаетесь к одному и тому же объекту Tempfile из нескольких потоков, защитите его с помощью мьютекса.
Общедоступные методы класса
# File lib/tempfile.rb, line 398
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 148
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
::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(self, Remover.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 326
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 182 def close(unlink_now=false) _close unlink if unlink_now end
Закрывает файл. Если unlink_now равно true, то файл будет удалён (удалён) после закрытия. Конечно, вы можете впоследствии вызвать unlink, если вы не удаляете его сейчас.
Если вы не удалите временный файл явно, удаление будет отложено до завершения объекта.
# File lib/tempfile.rb, line 189 def close! close(true) end
Закрывает и удаляет (удаляет) файл. Имеет тот же эффект, что и вызов close(true).
# File lib/tempfile.rb, line 164 def open _close mode = @mode & ~(File::CREAT|File::EXCL) @tmpfile = File.open(@tmpfile.path, mode, **@opts) __setobj__(@tmpfile) end
Открывает или повторно открывает файл в режиме «r+».
# File lib/tempfile.rb, line 242 def path @unlinked ? nil : @tmpfile.path end
Возвращает полное имя файла временного файла. Будет равно nil, если unlink был вызван.
# File lib/tempfile.rb, line 248
def size
if !@tmpfile.closed?
@tmpfile.size # File#size calls rb_io_flush_raw()
else
File.size(@tmpfile.path)
end
end Возвращает размер временного файла. В качестве побочного эффекта буфер IO сбрасывается перед определением размера.
# File lib/tempfile.rb, line 226
def unlink
return if @unlinked
begin
File.unlink(@tmpfile.path)
rescue Errno::ENOENT
rescue Errno::EACCES
# may not be able to unlink on Windows; just ignore
return
end
ObjectSpace.undefine_finalizer(self)
@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–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.