класс 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 или close в блоке ensure, например так:
file = Tempfile.new('foo')
begin
...do something with file...
ensure
file.close
file.unlink # deletes the temp file
end Удаление после создания
В системах POSIX можно удалить файл сразу после его создания и перед его закрытием. Это удаляет запись в файловой системе без закрытия дескриптора файла, поэтому гарантирует, что только процессы, которые уже открыли дескриптор файла, могут получить доступ к содержимому файла. Сильно рекомендуется делать это, если вы не хотите, чтобы другие процессы могли читать или писать в Tempfile, и вам также не нужно знать имя Tempfile.
Например, практический случай использования unlink-после-создания: вам нужен большой буфер байтов, слишком большой для удобного размещения в оперативной памяти, например, при написании веб-сервера, когда вы хотите буферизовать данные загрузки файла клиента.
Для получения дополнительной информации и примера кода см. unlink.
Дополнительные заметки
Метод выбора имени файла Tempfile является потокобезопасным и безопасным для межпроцессного взаимодействия: гарантирует, что другие потоки или процессы не выберут то же имя файла.
Однако сам Tempfile может не быть полностью потокобезопасным. Если вы получаете доступ к одному и тому же объекту Tempfile из нескольких потоков, то вы должны защитить его с помощью мьютекса.
Методы публичного класса
# File lib/tempfile.rb, line 326
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). Не использует finalizer и делегирование.
Если блок не задан, это аналогично ::new, за исключением создания File вместо Tempfile. Созданный файл не удаляется автоматически. Используйте File.unlink, чтобы удалить его.
Если блок задан, то будет создан объект File, и блок будет вызван с объектом в качестве аргумента. Объект File будет автоматически закрыт, а временный файл удалён после завершения блока. Вызов возвращает значение блока.
В любом случае, все аргументы (basename, tmpdir, mode, и **options) будут обработаны как в ::new.
Tempfile.create('foo', '/home/temp') do |f|
... do something with f ...
end # File lib/tempfile.rb, line 125
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 используется для определения имени временного файла. Можно передать строку или массив из 2 строк. В первом случае, имя временного файла будет начинаться с переданной строки. Во втором случае, имя временного файла будет начинаться с первого элемента массива и заканчиваться вторым. Например:
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. Когда $SAFE > 0 и переданный tmpdir является загрязнённым, используется '/tmp' как временный каталог. Обратите внимание, что значения ENV по умолчанию загрязнены, и возвращаемое значение Dir.tmpdir может исходить из переменных окружения (например, $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')
Исключения
Если ::new не может найти уникальное имя файла в ограниченном количестве попыток, то он выбросит исключение.
# File lib/tempfile.rb, line 289
def open(*args)
tempfile = new(*args)
if block_given?
begin
yield(tempfile)
ensure
tempfile.close
end
else
tempfile
end
end Создаёт новый Tempfile.
Если блок не задан, это синоним для ::new.
Если блок задан, то объект Tempfile будет создан, и блок будет выполнен с данным объектом в качестве аргумента. Объект Tempfile будет автоматически закрыт после завершения блока. Вызов возвращает значение блока.
В любом случае, все аргументы (*args) будут переданы в ::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 159 def close(unlink_now=false) _close unlink if unlink_now end
Закрывает файл. Если unlink_now равно true, то файл будет удалён (удалён) после закрытия. Конечно, вы можете позже вызвать unlink, если не удалили его сейчас.
Если вы не явно удалите временный файл, удаление будет отложено до финализации объекта.
# File lib/tempfile.rb, line 166 def close! close(true) end
Закрывает и удаляет файл. Имеет тот же эффект, что и вызванный close(true).
# File lib/tempfile.rb, line 141 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 219 def path @unlinked ? nil : @tmpfile.path end
Возвращает полное имя пути временного файла. Будет равно nil, если unlink был вызван.
# File lib/tempfile.rb, line 225
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 203
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 (раздел «Удаление после создания»); для получения дополнительной информации см. там.
Однако unlink-перед-закрытием может не поддерживаться в не-POSIX операционных системах. Наиболее заметным примером является Microsoft Windows: удаление не закрытого файла приведёт к ошибке, которую этот метод будет молча игнорировать. Если вы хотите применить unlink-перед-закрытием, когда это возможно, то ваш код должен выглядеть так:
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.