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
对象,那么您应该使用互斥锁保护它。
常量
- 版本
公共类方法
在底层文件系统中创建一个文件;返回一个基于该文件的新 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 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
在底层文件系统中创建一个文件;返回一个基于该文件的新的 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 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
受保护的类方法
创建一个新的 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
公共实例方法
关闭文件。如果 unlink_now
为真,则文件将在关闭后被解除链接(删除)。当然,您可以选择稍后调用 unlink
,如果您现在不解除链接它。
如果您没有显式地解除链接临时文件,则删除操作将延迟到对象被终结为止。
# File lib/tempfile.rb, line 208 def close(unlink_now=false) _close unlink if unlink_now end
关闭并解除链接(删除)文件。与调用 close(true)
具有相同的效果。
# File lib/tempfile.rb, line 215 def close! close(true) end
# File lib/tempfile.rb, line 174 def initialize_clone(other) initialize_copy_iv(other) super(other) ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end
# File lib/tempfile.rb, line 168 def initialize_dup(other) initialize_copy_iv(other) super(other) ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end
以“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
返回临时文件的完整路径名。如果已调用 unlink
,则此值为 nil。
# File lib/tempfile.rb, line 268 def path @unlinked ? nil : __getobj__.path end
返回临时文件的大小。作为副作用,在确定大小之前会刷新 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
从文件系统中取消链接(删除)文件。如“显式关闭”最佳实践部分在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
# File lib/tempfile.rb, line 252 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 ObjectSpace.undefine_finalizer(@finalizer_obj) @unlinked = true end
私有实例方法
# 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