class Tempfile
Eine Hilfsklasse zur Verwaltung temporärer Dateien.
Es gibt zwei Arten von Methoden zur Erstellung einer temporären Datei:
-
Tempfile.create(empfohlen) -
Tempfile.newundTempfile.open(hauptsächlich für die Abwärtskompatibilität, nicht empfohlen)
Tempfile.create erstellt ein normales File-Objekt. Der Zeitpunkt der Dateilöschung ist vorhersagbar. Außerdem unterstützt es die Open-and-Unlink-Technik, die die temporäre Datei sofort nach der Erstellung entfernt.
Tempfile.new und Tempfile.open erstellen ein Tempfile-Objekt. Die erstellte Datei wird vom GC (Finalizer) entfernt. Der Zeitpunkt der Dateilöschung ist nicht vorhersagbar.
Zusammenfassung
require 'tempfile' # Tempfile.create with a block # The filename are chosen 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
Über Tempfile.new und Tempfile.open
Dieser Abschnitt gilt nicht für Tempfile.create, da er ein File-Objekt (nicht ein Tempfile-Objekt) zurückgibt.
Wenn Sie ein Tempfile-Objekt erstellen, wird eine temporäre Datei mit einem eindeutigen Dateinamen erstellt. Ein Tempfile-Objekt verhält sich genau wie ein File-Objekt, und Sie können alle üblichen Dateioperationen darauf ausführen: Daten lesen, Daten schreiben, Berechtigungen ändern usw. Obwohl diese Klasse nicht alle von File unterstützten Instanzmethoden explizit dokumentiert, können Sie tatsächlich jede File-Instanzmethode auf einem Tempfile-Objekt aufrufen.
Ein Tempfile-Objekt verfügt über einen Finalizer zur Entfernung der temporären Datei. Das bedeutet, dass die temporäre Datei über GC entfernt wird. Dies kann mehrere Probleme verursachen:
-
Lange
GC-Intervalle und konservativesGCkönnen temporäre Dateien ansammeln, die nicht entfernt werden. -
Temporäre Dateien werden nicht entfernt, wenn Ruby abnormal beendet wird (z. B. SIGKILL, SEGV).
Es gibt bewährte Praktiken für Tempfile.new und Tempfile.open wie folgt:
Explizites Schließen
Wenn ein Tempfile-Objekt vom Garbage Collector übernommen wird oder wenn der Ruby-Interpreter beendet wird, wird die zugehörige temporäre Datei automatisch gelöscht. Das bedeutet, dass es nicht notwendig ist, ein Tempfile nach Gebrauch explizit zu löschen, obwohl es eine gute Praxis ist, dies zu tun: Das Nicht-explizite Löschen von ungenutzten Tempfiles kann potenziell eine große Anzahl von temporären Dateien auf dem Dateisystem hinterlassen, bis sie vom Garbage Collector übernommen werden. Die Existenz dieser temporären Dateien kann es schwieriger machen, einen neuen Tempfile-Dateinamen zu bestimmen.
Daher sollte man immer unlink oder close in einem ensure-Block aufrufen, wie hier:
file = Tempfile.new('foo') begin # ...do something with file... ensure file.close file.unlink # deletes the temp file end
Tempfile.create { ... } existiert zu diesem Zweck und ist bequemer zu verwenden. Beachten Sie, dass Tempfile.create eine File-Instanz anstelle eines Tempfile zurückgibt, was auch den Overhead und die Komplikationen der Delegation vermeidet.
Tempfile.create('foo') do |file| # ...do something with file... end
Unlink nach Erstellung
Auf POSIX-Systemen ist es möglich, eine Datei zu unliken, unmittelbar nachdem sie erstellt und bevor sie geschlossen wurde. Dies entfernt den Dateisystemeintrag, ohne den Dateihandle zu schließen, und stellt somit sicher, dass nur die Prozesse, die bereits den Dateihandle geöffnet hatten, auf den Inhalt der Datei zugreifen können. Es wird dringend empfohlen, dies zu tun, wenn Sie nicht möchten, dass andere Prozesse von der Tempfile lesen oder darauf schreiben können und Sie den Dateinamen der Tempfile nicht kennen müssen.
Dies garantiert auch, dass die temporäre Datei entfernt wird, selbst wenn Ruby abnormal beendet wird. Das Betriebssystem gibt den Speicher für die temporäre Datei wieder frei, wenn die Datei geschlossen wird oder der Ruby-Prozess normal oder abnormal beendet wird.
Ein praktischer Anwendungsfall für Unlink-nach-Erstellung wäre zum Beispiel: Sie benötigen einen großen Bytepuffer, der zu groß ist, um bequem in den RAM zu passen, z. B. wenn Sie einen Webserver schreiben und die Upload-Daten des Clients zwischenspeichern möchten.
`Tempfile.create(anonymous: true)` unterstützt dieses Verhalten. Es funktioniert auch unter Windows.
Kleinere Hinweise
Die Methode zur Auswahl des Dateinamens von Tempfile ist sowohl Thread-sicher als auch Prozess-sicher: Sie garantiert, dass keine anderen Threads oder Prozesse denselben Dateinamen auswählen.
Tempfile selbst ist möglicherweise nicht vollständig Thread-sicher. Wenn Sie vom selben Tempfile-Objekt aus mehreren Threads zugreifen, sollten Sie es mit einem Mutex schützen.
Constants
- VERSION
-
Die Version
Öffentliche Klassenmethoden
Source
# 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
Erstellt eine Datei im zugrunde liegenden Dateisystem; gibt ein neues File-Objekt zurück, das auf dieser Datei basiert.
Wenn kein Block gegeben und keine Argumente übergeben werden, wird eine Datei erstellt und zurückgegeben, deren
-
Klasse
Fileist (nicht Tempfile). -
Verzeichnis das temporäre Systemverzeichnis ist (systemabhängig).
-
Der generierte Dateiname ist in diesem Verzeichnis eindeutig.
-
Berechtigungen sind
0600; siehe Dateiberechtigungen. -
Der Modus ist
'w+'(Lese-/Schreibmodus, Position am Ende).
Die Entfernung der temporären Datei hängt vom Schlüsselwortargument anonymous und davon ab, ob ein Block gegeben wird oder nicht. Siehe die Beschreibung des Schlüsselwortarguments anonymous später.
Beispiel
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" }
Das Argument basename kann, falls gegeben, eines der folgenden sein:
-
Ein String: Der generierte Dateiname beginnt mit
basename.Tempfile.create('foo') # => #<File:/tmp/foo20220505-9795-1gok8l9>
-
Ein Array aus zwei Strings
[prefix, suffix]: Der generierte Dateiname beginnt mitprefixund endet mitsuffix.Tempfile.create(%w/foo .jpg/) # => #<File:/tmp/foo20220505-17839-tnjchh.jpg>
Mit den Argumenten basename und tmpdir wird die Datei im Verzeichnis tmpdir erstellt.
Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
Die Schlüsselwortargumente mode und options werden direkt an die Methode File.open übergeben.
-
Der Wert für
modemuss eine Ganzzahl sein und kann als logisches OR von Konstanten ausgedrückt werden, die inFile::Constantsdefiniert sind. -
Für
optionssiehe Open-Optionen.
Das Schlüsselwortargument anonymous gibt an, wann die Datei entfernt wird.
-
anonymous=false(Standard) ohne Block: Die Datei wird nicht entfernt. -
anonymous=false(Standard) mit Block: Die Datei wird nach Verlassen des Blocks entfernt. -
anonymous=trueohne Block: Die Datei wird vor der Rückgabe entfernt. -
anonymous=truemit Block: Die Datei wird vor dem Aufruf des Blocks entfernt.
Im ersten Fall (anonymous=false ohne Block) wird die Datei nicht automatisch entfernt. Sie sollte explizit geschlossen werden. Sie kann verwendet werden, um sie in den gewünschten Dateinamen umzubenennen. Wenn die Datei nicht benötigt wird, sollte sie explizit entfernt werden.
Die Methode File#path des erstellten Datei-Objekts gibt das temporäre Verzeichnis mit einem nachgestellten Schrägstrich zurück, wenn anonymous true ist.
Wenn ein Block gegeben wird, erstellt er die Datei wie oben beschrieben, übergibt sie an den Block und gibt den Wert des Blocks zurück. Vor der Rückgabe wird das Datei-Objekt geschlossen und die zugrunde liegende Datei entfernt.
Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"
Implementierungshinweis
Das Schlüsselwortargument anonymous=true wird unter Windows mithilfe von FILE_SHARE_DELETE implementiert. Unter Linux wird O_TMPFILE verwendet.
Verwandt: Tempfile.new.
Source
# 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
Erstellt eine Datei im zugrunde liegenden Dateisystem; gibt ein neues Tempfile-Objekt zurück, das auf dieser Datei basiert.
Wenn möglich, sollten Sie stattdessen Tempfile.create verwenden, das
-
Vermeidet die Kosten für die Delegation, die anfallen, wenn
Tempfile.newseine SuperklasseDelegateClass(File)aufruft. -
Verlässt sich nicht auf einen Finalizer zum Schließen und Unliken der Datei, was unzuverlässig sein kann.
Erstellt und gibt eine Datei zurück, deren
-
Klasse Tempfile ist (nicht File, wie bei
Tempfile.create). -
Verzeichnis das temporäre Systemverzeichnis ist (systemabhängig).
-
Der generierte Dateiname ist in diesem Verzeichnis eindeutig.
-
Berechtigungen sind
0600; siehe Dateiberechtigungen. -
Der Modus ist
'w+'(Lese-/Schreibmodus, Position am Ende).
Die zugrunde liegende Datei wird entfernt, wenn das Tempfile-Objekt stirbt und vom Garbage Collector übernommen wird.
Beispiel
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
Das Argument basename kann, falls gegeben, eines der folgenden sein:
-
Ein String: Der generierte Dateiname beginnt mit
basename.Tempfile.new('foo') # => #<Tempfile:/tmp/foo20220505-17839-1whk2f>
-
Ein Array aus zwei Strings
[prefix, suffix]: Der generierte Dateiname beginnt mitprefixund endet mitsuffix.Tempfile.new(%w/foo .jpg/) # => #<Tempfile:/tmp/foo20220505-17839-58xtfi.jpg>
Mit den Argumenten basename und tmpdir wird die Datei im Verzeichnis tmpdir erstellt.
Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>
Die Schlüsselwortargumente mode und options werden direkt an die Methode File.open übergeben.
-
Der Wert für
modemuss eine Ganzzahl sein und kann als logisches OR von Konstanten ausgedrückt werden, die inFile::Constantsdefiniert sind. -
Für
optionssiehe Open-Optionen.
Verwandt: Tempfile.create.
Source
# 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
Erstellt ein neues Tempfile.
Diese Methode wird nicht empfohlen und existiert hauptsächlich aus Gründen der Abwärtskompatibilität. Bitte verwenden Sie stattdessen Tempfile.create, das die Kosten der Delegation vermeidet, sich nicht auf einen Finalizer verlässt und die Datei auch beim Aufruf mit einem Block unlikt.
Tempfile.open ist immer noch geeignet, wenn Sie möchten, dass das Tempfile von einem Finalizer entfernt wird und Sie nicht explizit wissen können, wo im Programm das Tempfile sicher entfernt werden kann.
Wenn kein Block gegeben wird, ist dies ein Synonym für Tempfile.new.
Wenn ein Block gegeben wird, wird ein Tempfile-Objekt erstellt, und der Block wird mit dem Tempfile-Objekt als Argument ausgeführt. Das Tempfile-Objekt wird nach Beendigung des Blocks automatisch geschlossen. Die Datei wird jedoch **nicht** gelikt und muss manuell mit Tempfile#close! oder Tempfile#unlink gelikt werden. Der Finalizer versucht zwar, die Datei zu liken, aber darauf sollte man sich nicht verlassen, da dies dazu führen kann, dass die Datei viel länger auf der Festplatte verbleibt als beabsichtigt. Beispielsweise können Finalizer unter CRuby aufgrund von konservativer Stack-Scans und verbleibenden Referenzen im ungenutzten Speicher verzögert werden.
Der Aufruf gibt den Wert des Blocks zurück.
In jedem Fall werden alle Argumente (*args) an Tempfile.new übergeben.
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
Öffentliche Instanzmethoden
Source
# File lib/tempfile.rb, line 279 def close(unlink_now=false) _close unlink if unlink_now end
Schließt die Datei. Wenn unlink_now true ist, wird die Datei nach dem Schließen gelikt (gelöscht). Natürlich können Sie später unlink aufrufen, wenn Sie sie nicht jetzt liken.
Wenn Sie die temporäre Datei nicht explizit liken, wird die Entfernung verzögert, bis das Objekt finalisiert wird.
Source
# File lib/tempfile.rb, line 286 def close! close(true) end
Schließt und likt (löscht) die Datei. Hat die gleiche Wirkung wie ein Aufruf von close(true).
Source
# 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
Öffnet oder öffnet die Datei neu im Modus „r+“.
Source
# File lib/tempfile.rb, line 341 def path @unlinked ? nil : __getobj__.path end
Gibt den vollständigen Pfadnamen der temporären Datei zurück. Dies ist nil, wenn unlink aufgerufen wurde.
Source
# 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
Gibt die Größe der temporären Datei zurück. Als Nebeneffekt wird der IO-Puffer geleert, bevor die Größe ermittelt wird.
Source
# 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
Likt (löscht) die Datei aus dem Dateisystem. Man sollte die Datei immer nach Gebrauch liken, wie im Abschnitt „Explizites Schließen“ der Tempfile-Übersicht erläutert.
file = Tempfile.new('foo') begin # ...do something with file... ensure file.close file.unlink # deletes the temp file end
Unlink vor Schließen
Auf POSIX-Systemen ist es möglich, eine Datei zu liken, bevor sie geschlossen wird. Diese Vorgehensweise wird im Überblick über Tempfile (Abschnitt „Unlink nach Erstellung“) detailliert erklärt; bitte verweisen Sie dort für weitere Informationen.
Unlink vor Schließen wird auf Nicht-POSIX-Betriebssystemen möglicherweise nicht unterstützt. Microsoft Windows ist der bemerkenswerteste Fall: Das Liken einer nicht geschlossenen Datei führt zu einem Fehler, den diese Methode stillschweigend ignoriert. Wenn Sie die Vorgehensweise „Unlink vor Schließen“ so oft wie möglich anwenden möchten, sollten Sie Code wie diesen schreiben:
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