класс Tempfile
Утилитарный класс для управления временными файлами.
Существует два способа создания временного файла:
-
Tempfile.create(рекомендуется) -
Tempfile.newиTempfile.open(в основном для обратной совместимости, не рекомендуется)
Tempfile.create создаёт обычный объект File. Время удаления файла предсказуемо. Также он поддерживает технику open-and-unlink, которая удаляет временный файл сразу после создания.
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, вы можете фактически вызвать любой метод экземпляра File на объекте Tempfile.
Объект Tempfile имеет финализатор для удаления временного файла. Это означает, что временный файл удаляется посредством GC. Это может привести к нескольким проблемам:
-
Длительные интервалы
GCи консервативныеGCмогут накапливать временные файлы, которые не удаляются. -
Временные файлы не удаляются, если Ruby завершается аномально (например, SIGKILL, SEGV).
Существуют устаревшие рекомендации для Tempfile.new и Tempfile.open.
Явное закрытие
Когда объект 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
Tempfile.create { … } существует для этой цели и удобнее в использовании. Обратите внимание, что Tempfile.create возвращает экземпляр File вместо Tempfile, что также избегает накладных расходов и проблем с делегированием.
Tempfile.create('foo') do |file|
# ...do something with file...
end
Удаление после создания
В системах POSIX можно удалить файл сразу после его создания и до закрытия. Это удаляет запись в файловой системе без закрытия дескриптора файла, что гарантирует, что только процессы, которые уже имели открытый дескриптор файла, могут получить доступ к содержимому файла. Это настоятельно рекомендуется, если вы не хотите, чтобы другие процессы могли читать или писать в Tempfile, и вам также не нужно знать имя файла Tempfile.
Также это гарантирует, что временный файл будет удален, даже если Ruby завершится аномально. ОС освобождает место для временного файла при закрытии файла или при завершении процесса Ruby (нормально или аномально).
Например, практический случай использования unlink-after-creation: вам нужен большой буфер байтов, который слишком велик, чтобы комфортно поместиться в оперативной памяти, например, когда вы пишете веб-сервер и хотите буферизовать данные загружаемого файлом клиента.
‘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, основанный на этом файле.
Без блока и аргументов создаёт и возвращает файл, у которого:
-
Директория — системный временный каталог (зависит от системы).
-
Сгенерированное имя файла уникально в этой директории.
-
Права доступа —
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. O_TMPFILE используется в Linux.
Связанные методы: 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). -
Не полагается на финализатор для закрытия и удаления файла, что может быть ненадежно.
Создаёт и возвращает файл, у которого:
-
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 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
Возвращает полное имя пути временного файла. Будет равно null, если unlink был вызван.
Исходный код
# 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–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.