класс 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
Удаление после создания
В системах POSIX возможно удалить файл сразу после его создания и до закрытия. Это удаляет запись в файловой системе без закрытия дескриптора файла, гарантируя, что только процессы, которые уже открыли дескриптор файла, могут получить доступ к содержимому файла. Это настоятельно рекомендуется, если вы не хотите, чтобы другие процессы могли читать или записывать в Tempfile, и вам также не нужно знать имя файла Tempfile.
Например, практическое применение удаления после создания - это необходимость в большом буфере байтов, который слишком велик для комфортного размещения в оперативной памяти, например, при написании веб-сервера, когда вам нужно буферизовать данные загрузки файла клиентом.
Дополнительную информацию и пример кода см. в unlink.
Дополнительные замечания
Метод выбора имени файла Tempfile является безопасным для потоков и безопасным для межпроцессного взаимодействия: он гарантирует, что ни один другой поток или процесс не выберет то же имя файла.
Однако сам Tempfile может не быть полностью безопасным для потоков. Если вы получаете доступ к одному и тому же объекту Tempfile из нескольких потоков, то вы должны защитить его с помощью мьютекса.
Методы открытого класса
# File lib/tempfile.rb, line 323
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 (а не Tempfile). Он не использует финализатор и делегирование.
Если блок не указан, это аналогично Tempfile.new, за исключением создания объекта File вместо Tempfile. Созданный файл не удаляется автоматически. Для его удаления используйте File.unlink.
Если блок указан, то будет создан объект File, и блок будет вызван с этим объектом в качестве аргумента. Объект File будет автоматически закрыт, а временный файл удалён после завершения блока. Вызов возвращает значение блока.
В любом случае все аргументы (basename, tmpdir, mode, и **options) будут обработаны как в Tempfile.new.
Tempfile.create('foo', '/home/temp') do |f|
# ... do something with f ...
end
# File lib/tempfile.rb, line 122
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 Создаёт временный файл с правами 0600 (=только чтение и запись владельцем) и открывает его в режиме “w+”.
Параметр basename используется для определения имени временного файла. Можно передать строку String или массив Array из 2 элементов типа String. В первом случае имя временного файла начнется с переданной строки. Во втором случае – с первого элемента массива, а закончится вторым. Например:
file = Tempfile.new('hello')
file.path # => something like: "/tmp/hello2843-8392-92849382--0"
# Use the Array form to enforce an extension in the filename:
file = Tempfile.new(['hello', '.jpg'])
file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
Временный файл будет помещён в каталог, указанный параметром tmpdir. По умолчанию это Dir.tmpdir.
file = Tempfile.new('hello', '/home/aisaka')
file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0"
Также можно передать хеш опций. Под капотом, Tempfile создает временный файл используя File.open. Эти опции будут переданы File.open . Это полезно в основном для задания кодировки, например:
Tempfile.new('hello', '/home/aisaka', encoding: 'ascii-8bit')
# You can also omit the 'tmpdir' parameter:
Tempfile.new('hello', encoding: 'ascii-8bit')
Примечание: ключевой аргумент mode, принятый Tempfile, может быть только числовым, комбинацией режимов, определённых в File::Constants.
Исключения
Если Tempfile.new не может найти уникальное имя файла за ограниченное количество попыток, то он генерирует исключение.
# File lib/tempfile.rb, line 286
def open(*args, **kw)
tempfile = new(*args, **kw)
if block_given?
begin
yield(tempfile)
ensure
tempfile.close
end
else
tempfile
end
end Создаёт новый объект Tempfile.
Если блок не указан, это синоним для Tempfile.new.
Если блок указан, то объект Tempfile будет создан, и блок будет выполнен с этим объектом в качестве аргумента. Объект Tempfile будет автоматически закрыт после завершения блока. Вызов возвращает значение блока.
В любом случае все аргументы (*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 156 def close(unlink_now=false) _close unlink if unlink_now end
Закрывает файл. Если unlink_now равно true, то файл будет удалён (удалён) после закрытия. Конечно, вы можете позже вызвать unlink, если не хотите удалять его сейчас.
Если вы явно не удалите временный файл, удаление будет отложено до финализации объекта.
# File lib/tempfile.rb, line 163 def close! close(true) end
Закрывает и удаляет файл. Имеет тот же эффект, что и вызов close(true).
# File lib/tempfile.rb, line 138 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 216 def path @unlinked ? nil : @tmpfile.path end
Возвращает полное имя временного файла. Будет равно nil, если был вызван unlink.
# File lib/tempfile.rb, line 222
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 200
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–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.