Tempfile 类

一个用于管理临时文件的实用程序类。当你创建一个 Tempfile 对象时,它将创建一个具有唯一文件名的临时文件。一个 Tempfile 对象的行为就像一个 File 对象一样,你可以对它执行所有常见的文件操作:读取数据、写入数据、更改其权限等。因此,虽然这个类没有明确地记录 File 支持的所有实例方法,但实际上你可以在 Tempfile 对象上调用任何 File 实例方法。

概述

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,尽管这是一个好习惯:不显式删除未使用的 Tempfiles 可能会在它们被垃圾回收之前在文件系统上留下大量临时文件。这些临时文件的存在会使确定新的 Tempfile 文件名变得更加困难。

因此,应该始终在 ensure 块中调用 unlink 或 close,例如

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-after-creation 的一个实际用例是:您需要一个大型字节缓冲区,它太大而无法舒适地放入 RAM 中,例如,当您编写 Web 服务器并且想要缓冲客户端的文件上传数据时。

有关更多信息和代码示例,请参阅 unlink

次要说明

Tempfile 的文件名选择方法既是线程安全的,又是进程间安全的:它保证没有其他线程或进程会选择相同的文件名。

Tempfile 本身可能不是完全线程安全的。如果您从多个线程访问同一个 Tempfile 对象,那么您应该使用互斥锁保护它。

常量

版本

公共类方法

create(basename="", tmpdir=nil, mode: 0, **options) { |tmpfile| ... } 点击切换源代码

在底层文件系统中创建一个文件;返回一个基于该文件的新 File 对象。

如果没有给出代码块并且没有参数,则创建并返回一个文件,其

  • ClassFile(而不是 Tempfile)。

  • 目录是系统临时目录(系统相关)。

  • 生成的文件名在该目录中是唯一的。

  • 权限为 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>
    

使用参数 basenametmpdir,文件将在目录 tmpdir 中创建

Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>

关键字参数 modeoptions 会直接传递给方法 File.open

  • mode 的值必须是整数,可以表示为在 File::Constants 中定义的常量的逻辑或运算。

  • 有关 options,请参阅 打开选项

如果给定一个代码块,则按上述方式创建文件,将其传递给代码块,并返回代码块的值;在返回之前,文件对象将被关闭,底层文件将被删除。

Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"

相关:Tempfile.new

# 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
new(basename="", tmpdir=nil, mode: 0, **options) 点击切换源代码

在底层文件系统中创建一个文件;返回一个基于该文件的新的 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>
    

使用参数 basenametmpdir,文件将在目录 tmpdir 中创建

Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>

关键字参数 modeoptions 会直接传递给方法 File.open

  • mode 的值必须是整数,可以表示为在 File::Constants 中定义的常量的逻辑或运算。

  • 有关 options,请参阅 打开选项

相关:Tempfile.create

调用超类方法
# 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

受保护的类方法

open(*args, **kw) { |tempfile| ... } 点击切换源代码

创建一个新的 Tempfile

不推荐使用此方法,它主要用于向后兼容。请改用 Tempfile.create,它可以避免委托成本,不依赖于最终器,并且在给定代码块时还会解除文件链接。

如果需要通过最终器解除 Tempfile 的链接,并且无法明确知道在程序中的哪个位置可以安全地解除 Tempfile 的链接,则 Tempfile.open 仍然适用。

如果没有给出代码块,则它等同于 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 366
def open(*args, **kw)
  tempfile = new(*args, **kw)

  if block_given?
    begin
      yield(tempfile)
    ensure
      tempfile.close
    end
  else
    tempfile
  end
end

公共实例方法

close(unlink_now=false) 点击切换源代码

关闭文件。如果 unlink_now 为真,则文件将在关闭后被解除链接(删除)。当然,您可以选择稍后调用 unlink,如果您现在不解除链接它。

如果您没有显式地解除链接临时文件,则删除操作将延迟到对象被终结为止。

# File lib/tempfile.rb, line 208
def close(unlink_now=false)
  _close
  unlink if unlink_now
end
close!() 点击切换源代码

关闭并解除链接(删除)文件。与调用 close(true) 具有相同的效果。

# File lib/tempfile.rb, line 215
def close!
  close(true)
end
delete()
别名:unlink
initialize_clone(other) 点击切换源代码
调用超类方法
# File lib/tempfile.rb, line 174
def initialize_clone(other)
  initialize_copy_iv(other)
  super(other)
  ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
end
initialize_dup(other) 点击切换源代码
调用超类方法
# File lib/tempfile.rb, line 168
def initialize_dup(other)
  initialize_copy_iv(other)
  super(other)
  ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
end
length()
别名:size
open() 点击切换源代码

以“r+”模式打开或重新打开文件。

# 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
path() 点击切换源代码

返回临时文件的完整路径名。如果已调用 unlink,则此值为 nil。

# File lib/tempfile.rb, line 268
def path
  @unlinked ? nil : __getobj__.path
end
size() 点击切换源代码

返回临时文件的大小。作为副作用,在确定大小之前会刷新 IO 缓冲区。

# 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
也称为:length

私有实例方法

initialize_copy_iv(other) 点击以切换源代码
# 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