Dateien in Ruby filtern: ein Praxisleitfaden
Effizientes Filtern von Dateien ist unverzichtbar, von der Logverarbeitung bis zur Medienverwaltung. Ruby bietet robuste Werkzeuge zur Dateiauswahl, die über einfache Mustervergleiche hinausgehen. Entdecken Sie praktische Techniken für einfache bis anspruchsvolle Szenarien.
Voraussetzungen
Die folgenden Beispiele wurden mit Ruby 3.4.10 und mime-types 3.6.0 sowie dem Verzeichnis mime-types-data 3.2024.0806 getestet. Nutzen Sie für neue Projekte eine gepflegte Ruby-Version. Nur das Beispiel zur MIME-Abfrage benötigt das Gem mime-types. Ergänzen Sie in einem bestehenden Bundler-Projekt Folgendes:
# Add to your Gemfile
gem 'mime-types', '3.6.0'
Führen Sie bundle install aus und starten Sie Ihre Skripte anschließend mit
bundle exec ruby script.rb. Ohne Bundler installieren Sie dieselbe Version direkt:
gem install mime-types -v 3.6.0
Speichern Sie die Ruby-Beispiele in einer Datei mit der Endung .rb
und führen Sie sie mit ruby script.rb aus. Ersetzen Sie die Beispielverzeichnisse
durch Ihre eigenen Pfade. Die Ergebnisse sind Arrays oder Enumeratoren. Nutzen Sie
p, um sie anzusehen, etwa p recent_images
nach dem vollständigen Beispiel zu FileFilter.
Das Gem mime-types verwendet eine angepasste semantische Versionierung, um sowohl API-Änderungen als auch Aktualisierungen der Verzeichnisdaten abzubilden. Weitere Details finden Sie in der Dokumentation zu mime-types.
Grundlegende Filtermethoden
Die Standardbibliothek von Ruby bietet mehrere direkt einsetzbare Lösungen zum Filtern von Dateien:
# Filter by extension
docs_dir = '/docs'
pdf_files = Dir.children(docs_dir).select do |f|
File.file?(File.join(docs_dir, f)) && File.extname(f) == '.pdf'
end
# Filter by size (1MB threshold)
large_files = Dir.glob('*').select do |f|
begin
File.file?(f) && File.size(f) > 1_000_000
rescue Errno::ENOENT, Errno::EACCES => e
warn "Error accessing #{f}: #{e.message}"
false
end
end
# Filter by modification time (last 24 hours)
recent_files = Dir.glob('*').select do |f|
begin
File.file?(f) && File.mtime(f) > (Time.now - 86400)
rescue Errno::ENOENT, Errno::EACCES => e
warn "Error accessing #{f}: #{e.message}"
false
end
end
Diese Methoden nutzen die Hilfsfunktionen der Klasse File für schnelle
Prüfungen, ohne Dateiinhalte zu laden. Die Prüfung der Dateiendung liefert Namen relativ zu
docs_dir und unterscheidet Groß- und Kleinschreibung. Die Glob-Beispiele
liefern Pfade relativ zum aktuellen Arbeitsverzeichnis und lassen Dateien mit führendem Punkt aus.
File.file? schließt Verzeichnisse aus, folgt aber symbolischen Links auf reguläre
Dateien. Diese Prüfungen sind keine Dateisystem-Sandbox. Die Zeitfilter verwenden eine untere
Zeitgrenze, sodass auch Dateien mit einem Datum in der Zukunft die Kriterien erfüllen.
Erweiterter Mustervergleich mit Glob
Ruby unterstützt mit Dir.glob Mustervergleiche im UNIX-Stil mit einigen
Ruby-spezifischen Erweiterungen:
# Match nested Markdown files
markdown_files = Dir.glob('**/*.md').select { |f| File.file?(f) }
# Match names containing a January 2024 date (not modification times)
jan_files = Dir.glob('*').grep(/(2024-01-\d{2})/).select { |f| File.file?(f) }
# Combined size and type filter
big_images = Dir.glob('*.{jpg,png}').select do |f|
begin
File.file?(f) && File.size(f) > 500_000
rescue Errno::ENOENT, Errno::EACCES => e
warn "Error accessing #{f}: #{e.message}"
false
end
end
Nutzen Sie den doppelten Stern (**) zum rekursiven Durchlaufen von
Verzeichnissen und die Erweiterung geschweifter Klammern für mehrere Dateiendungen.
MIME-Typen abfragen
Nutzen Sie das Gem mime-types, um den Medientyp zu einer Dateiendung abzufragen:
require 'mime/types'
def media_files(dir)
Dir.children(dir).select do |f|
begin
next unless File.file?(File.join(dir, f))
MIME::Types.type_for(f).any? do |mime|
mime.media_type == 'image' || mime.media_type == 'video'
end
rescue StandardError => e
warn "Error processing #{f}: #{e.message}"
false
end
end
end
# Usage:
visual_assets = media_files('/content/assets')
Dieser Ansatz nutzt ein Verzeichnis von Dateiendungen. Er untersucht weder Dateiinhalte noch prüft
er, ob eine Datei sicher ist. Für einen Dateinamen können mehrere Typen registriert sein:
.mp4 hat im getesteten Verzeichnis beispielsweise sowohl Application- als
auch Video-Treffer. Wenn Sie jeden Treffer prüfen, wird die Datei nicht allein deshalb ausgeschlossen,
weil der erste Treffer kein Videotyp ist. Auch das belegt jedoch nicht, dass die Datei Video enthält.
Untersuchen Sie die Inhalte separat, wenn Sie nicht vertrauenswürdige Uploads annehmen.
Weitere Details finden Sie in der
Dokumentation zum Gem mime-types.
Nach Metadaten filtern
Kombinieren Sie mehrere Metadaten für eine präzise Auswahl:
def recent_documents(path)
Dir.glob('*', base: path).map { |name| File.join(path, name) }.select do |f|
begin
next unless File.file?(f)
ext = File.extname(f).downcase
size = File.size(f)
modified = File.mtime(f)
(ext == '.pdf' || ext == '.docx') &&
size.between?(10_000, 5_000_000) &&
modified > (Time.now - 7*86400)
rescue StandardError => e
warn "Error processing #{f}: #{e.message}"
false
end
end
end
Dies wählt PDF/DOCX-Dateien direkt in path aus, ohne bei den Dateiendungen
Groß- und Kleinschreibung zu unterscheiden. Die Dateigröße liegt zwischen 10.000 und 5.000.000 Bytes,
einschließlich beider Grenzen. Dateien, die vor genau sieben Tagen geändert wurden, sind ausgeschlossen.
Wenn Sie das Verzeichnis als base: übergeben, bleiben Zeichen wie
[ in seinem Namen außerhalb des Glob-Musters.
Hinweise zur Performance
Bei der Verarbeitung großer Verzeichnisse:
-
Verzögerte Auswertung: Verschieben Sie Metadatenprüfungen mit
lazy.Dir.globerstellt weiterhin sein Ergebnis-Array.Dir.glob('**/*').lazy .select { |f| File.file?(f) && File.size(f) > 1_000_000 } .first(10) -
Frühzeitiger Abbruch: Brechen Sie nach Möglichkeit mit
breakfrühzeitig ablog_dir = '/logs' Dir.children(log_dir).each do |name| f = File.join(log_dir, name) begin next unless File.file?(f) && f.end_with?('.log') break if File.size(f) > 1_000_000_000 # Stop at first huge log process_log(f) rescue StandardError => e warn "Error processing #{f}: #{e.message}" end endDefinieren Sie
process_log(path)für die Verarbeitung in Ihrer Anwendung, bevor Sie diesen Ausschnitt ausführen. -
Metadaten zwischenspeichern: Speichern Sie häufig abgerufene Daten zwischen
file_cache = {} Dir.glob('*').each do |f| begin file_cache[f] = { mtime: File.mtime(f), size: File.size(f) } rescue StandardError => e warn "Error caching #{f}: #{e.message}" end end
Beispiel für den Produktionseinsatz
Diese wiederverwendbare Auswahlfunktion liefert einen Enumerator mit Pfaden unterhalb des angegebenen Verzeichnisses:
class FileFilter
def initialize(root_dir)
@root = root_dir
end
def find_files(extensions: [], min_size: 0, max_age: Float::INFINITY)
Dir.glob('**/*', base: @root).lazy.map { |name| File.join(@root, name) }.select do |path|
begin
next unless File.file?(path)
valid_extension = extensions.empty? || extensions.include?(File.extname(path))
valid_size = File.size(path) >= min_size
valid_age = (Time.now - File.mtime(path)) < max_age
valid_extension && valid_size && valid_age
rescue StandardError => e
warn "Error processing #{path}: #{e.message}"
false
end
end
end
end
# Usage:
filter = FileFilter.new('/user/uploads')
recent_images = filter.find_files(
extensions: ['.jpg', '.png'],
min_size: 100_000,
max_age: 3600 # 1 hour
).first(100)
Das Anwendungsbeispiel wählt bis zu 100 Pfade mit der Endung .jpg oder
.png aus. Die Dateien sind mindestens 100.000 Bytes groß und wurden vor
weniger als einer Stunde geändert. Der Vergleich der Dateiendungen unterscheidet hier Groß- und
Kleinschreibung. Die Suche erstellt weiterhin ein Glob-Array;
lazy verzögert die Metadatenprüfungen, nicht das Durchlaufen der Verzeichnisse.
Versteckte Dateien werden ausgelassen, symbolischen Links auf Dateien wird gefolgt, und symbolisch
verlinkte Verzeichnisse werden nicht rekursiv durchlaufen. Ein fehlendes Ausgangsverzeichnis liefert
keine Treffer. Dateien können sich nach der Auswahl ändern. Behandeln Sie Fehler daher erneut beim
Öffnen der Dateien.
Umgang mit Zeichenkodierungen von Dateinamen
Ruby verarbeitet die Zeichenkodierung von Dateinamen bei Verwendung von UTF-8 normalerweise automatisch. Falls jedoch Probleme mit Nicht-ASCII-Zeichen in Dateinamen auftreten, erstellen Sie wie folgt einen UTF-8-Wert für die Anzeige:
# Convert from Ruby's known source encoding and replace invalid bytes for display
utf8_labels = Dir.glob('*').map do |f|
f.encode('UTF-8', invalid: :replace, undef: :replace).scrub
end
Behalten Sie den ursprünglichen Pfad für Dateisystemoperationen bei. Ersatzzeichen können einen
Dateinamen verändern; diese Bezeichnungen dienen nur der Anzeige. force_encoding
weist Bytes lediglich eine andere Zeichenkodierung zu und transkodiert sie nicht.
Fazit
In diesem Leitfaden haben wir praktische Techniken zum Filtern von Dateien in Ruby vorgestellt: von einfachen Prüfungen der Dateiendung und Glob-Mustern bis zur dateinamenbasierten MIME-Abfrage und Metadatenfiltern. Kombinieren Sie diese Methoden, um effiziente Pipelines zur Dateiverarbeitung für Ihre Anforderungen zu entwickeln. Für produktionsreife Lösungen im großen Maßstab lohnt sich ein Blick auf die robuste API zur Dateiverarbeitung von Transloadit. Weitere Informationen finden Sie in unserer Dokumentation.
