Knoten verfügt über eine Reihe von Datenstrom-APIs, die Dateien wie Netzwerkströme verarbeiten können. Die Verwendung ist sehr praktisch, ermöglicht jedoch nur die sequentielle Verarbeitung von Dateien und kann keine Dateien zufällig lesen und schreiben. Daher müssen einige Dateisystemvorgänge auf niedrigerer Ebene verwendet werden.
In diesem Kapitel werden die Grundlagen der Dateiverarbeitung behandelt, einschließlich des Öffnens einer Datei, des Lesens eines Teils der Datei, des Schreibens von Daten und des Schließens der Datei.
Viele der Datei-APIs von Node sind nahezu Replikate der entsprechenden Datei-APIs in UNIX (POSIX). Die Verwendung von Dateideskriptoren ist beispielsweise genau wie in UNIX. Der Dateideskriptor in Node ist ebenfalls eine Ganzzahl, die eine darstellt Entität. Index in die Prozessdatei-Deskriptortabelle.
Es gibt 3 spezielle Dateideskriptoren – 1, 2 und 3. Sie repräsentieren jeweils Standardeingabe-, Standardausgabe- und Standardfehlerdateideskriptoren. Bei der Standardeingabe handelt es sich, wie der Name schon sagt, um einen schreibgeschützten Stream, den Prozesse zum Lesen von Daten aus der Konsole oder dem Prozesskanal verwenden. Standardausgabe und Standardfehler sind Dateideskriptoren, die nur zur Ausgabe von Daten verwendet werden. Sie werden häufig zur Ausgabe von Daten an die Konsole, andere Prozesse oder Dateien verwendet. Der Standardfehler ist für die Ausgabe von Fehlerinformationen verantwortlich, und die Standardausgabe ist für die normale Prozessausgabe verantwortlich.
Sobald der Prozess gestartet ist, können diese Dateideskriptoren verwendet werden. Sie verfügen nicht über entsprechende physische Dateien. Sie können Daten nicht an einer zufälligen Position lesen und schreiben (Anmerkung des Übersetzers: Der Originaltext lautet: Sie können an bestimmten Positionen in der Datei schreiben und von dort aus lesen. Je nach Kontext hat der Autor möglicherweise ein „nicht“ übersehen). Sie können das Netzwerk nur so betreiben, dass die Daten wie ein Datenstrom sequentiell gelesen und ausgegeben werden und die geschriebenen Daten nicht geändert werden können.
Gewöhnliche Dateien unterliegen dieser Einschränkung nicht. In Node können Sie beispielsweise Dateien erstellen, die nur Daten an das Ende anhängen können, und Sie können auch Dateien erstellen, die zufällige Speicherorte lesen und schreiben.
Fast alle dateibezogenen Vorgänge umfassen die Verarbeitung von Dateipfaden. In diesem Kapitel werden zunächst diese Werkzeugfunktionen vorgestellt und anschließend ausführliche Lese-, Schreib- und Datenvorgänge für Dateien erläutert
Dateipfade werden verarbeitet
Dateipfade werden in relative Pfade und absolute Pfade unterteilt, die zur Darstellung bestimmter Dateien verwendet werden. Sie können Dateipfade zusammenführen, Dateinameninformationen extrahieren und sogar erkennen, ob eine Datei vorhanden ist.
In Node können Sie Zeichenfolgen verwenden, um Dateipfade zu verarbeiten, aber das wird das Problem verkomplizieren. Wenn Sie beispielsweise verschiedene Teile des Pfads verbinden möchten, enden einige Teile mit „/“ und andere nicht, und das Pfadtrennzeichen sind möglicherweise auch unterschiedlich. Wenn Sie sie verbinden, ist der Code daher sehr wortreich und umständlich.
Glücklicherweise verfügt Node über ein Modul namens „Pfad“, das Ihnen dabei helfen kann, Pfade zu standardisieren, zu verbinden, zu analysieren, von absoluten Pfaden in relative Pfade zu konvertieren, verschiedene Teile von Informationen aus dem Pfad zu extrahieren und zu erkennen, ob die Datei vorhanden ist. Im Allgemeinen ist das Pfadmodul eigentlich nur eine Zeichenfolgenverarbeitung und wird nicht zur Überprüfung an das Dateisystem weitergeleitet (mit Ausnahme der Funktion path.exists).
Standardisierung von Pfaden
Normalerweise ist es eine gute Idee, Pfade zu normalisieren, bevor Sie sie speichern oder verwenden. Beispielsweise sollten Dateipfade, die durch Benutzereingaben oder Konfigurationsdateien erhalten werden, oder Pfade, die durch zwei oder mehr Pfade verbunden sind, im Allgemeinen standardisiert werden. Sie können die Normalisierungsfunktion des Pfadmoduls verwenden, um einen Pfad zu normalisieren, und sie kann auch „..“, „.“ und „//“ verarbeiten. Zum Beispiel:
path.normalize('/foo/bar//baz/asdf/quux/..');
// => '/foo/bar/baz/asdf'
Verbindungspfad
Mit der Funktion path.join() können Sie beliebig viele Pfadzeichenfolgen verbinden. Übergeben Sie einfach alle Pfadzeichenfolgen der Reihe nach an die Funktion join():
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');
// =>
Wie Sie sehen können, normalisiert path.join() den Pfad automatisch intern.
Pfad analysieren
Verwenden Sie path.resolve(), um mehrere Pfade in einen absoluten Pfad aufzulösen. Seine Funktion ähnelt der Durchführung einer „cd“-Operation nacheinander für diese Pfade. Im Gegensatz zu den Parametern des cd-Befehls können diese Pfade Dateien sein und müssen nicht tatsächlich existieren – die Methode path.resolve() greift nicht darauf zu Das zugrunde liegende Dateisystem. Stellen Sie fest, ob der Pfad vorhanden ist. Es handelt sich lediglich um eine Zeichenfolgenmanipulation.
Zum Beispiel:
path.resolve('/foo/bar', './baz');
// =>
path.resolve('/foo/bar', '/tmp/file/');// =>
path.relative() kann Ihnen sagen, wie Sie von einer absoluten Adresse zu einer anderen springen, wie zum Beispiel:
// =>
Nehmen Sie den Pfad „/foo/bar/myfile.txt“ als Beispiel, wenn Sie den gesamten Inhalt des übergeordneten Verzeichnisses (/foo/bar) abrufen oder andere Dateien im selben Verzeichnis lesen möchten , Sie müssen path.dirname(filePath) verwenden, um den Verzeichnisteil des Dateipfads abzurufen, z. B.:
Oder wenn Sie den Dateinamen aus dem Dateipfad erhalten möchten, der der letzte Teil des Dateipfads ist, können Sie die Funktion path.basename verwenden:
Code kopieren
Der Dateipfad kann auch die Dateierweiterung enthalten, die normalerweise der Teil der Zeichenfolge nach dem letzten „.“ ist.
path.basename kann auch eine Erweiterungszeichenfolge als zweiten Parameter akzeptieren, sodass der zurückgegebene Dateiname automatisch die Erweiterung entfernt und nur den Namensteil der Datei zurückgibt:
path.basename('/foo/bar/baz/asdf/quux.html', '.html');
// =>
path.extname('/a/b.c/index');
// =>
path.extname('/a/b.c/.');// =>
path.extname('/a/b.c/d.');
// =>
Überprüfen Sie, ob der Pfad existiert
Code kopieren
});
path.exists('/does_not_exist', function(exists) {
console.log('exists:', existiert);
});
Hinweis: Ab der Node-Version 0.8 wurde „exists“ vom Pfadmodul in das fs-Modul verschoben und in „fs.exists“ geändert. Außer dem Namespace hat sich nichts anderes geändert:
fs.exists('/does_not_exist', function(exists) {
console.log('exists:', existiert);
});
// => true
fs-Moduleinführung
Das fs-Modul enthält alle zugehörigen Funktionen zur Dateiabfrage und -verarbeitung. Mit diesen Funktionen können Sie Dateiinformationen abfragen, Dateien lesen, schreiben und schließen. Importieren Sie das fs-Modul wie folgt:
Dateiinformationen abfragen
Manchmal müssen Sie möglicherweise die Dateigröße, das Erstellungsdatum oder die Berechtigungen und andere Dateiinformationen kennen. Sie können die Funktion fs.stath verwenden, um die Metainformationen der Datei oder des Verzeichnisses abzufragen:
if (err) { throw err;}
console.log(stats);
});
Modus: 33188,
nlink: 1,
UID: 0,
gid: 0,
rdev: 0,
Größe: 5086,
Blockgröße: 4096,
Blöcke: 0,
Uhrzeit: Fr, 18. November 2011 22:44:47 GMT,
mzeit: Do, 08. September 2011 23:50:04 GMT,
ctime: Do, 08. September 2011 23:50:04 GMT 🎜>
1. Der fs.stat()-Aufruf übergibt eine Instanz der Statistikklasse als Parameter an ihre Rückruffunktion. Sie können die Statistikinstanz wie folgt verwenden:
2.stats.isFile() – Gibt true zurück, wenn es sich um eine Standarddatei handelt, nicht um ein Verzeichnis, einen Socket, einen symbolischen Link oder ein Gerät, andernfalls false
3.stats.isDiretory() – Gibt true zurück, wenn es sich um ein Verzeichnis handelt, andernfalls false
4.stats.isBlockDevice() – Gibt true zurück, wenn es sich um ein Blockgerät handelt. In den meisten UNIX-Systemen befinden sich Blockgeräte normalerweise im /dev-Verzeichnis
5.stats.isChracterDevice() – Gibt true zurück, wenn es sich um ein Zeichengerät handelt
6.stats.isSymbolickLink() – Gibt true zurück, wenn es sich um einen Dateilink handelt
7.stats.isFifo() – Gibt „true“ zurück, wenn es sich um einen FIFO (einen speziellen Typ einer UNIX-Named Pipe) handelt
8.stats.isSocket() – wenn es ein UNIX-Socket ist (TODO: googe it)
Datei öffnen
Bevor Sie eine Datei lesen oder verarbeiten, müssen Sie zunächst die Funktion fs.open verwenden, um die von Ihnen bereitgestellte Rückruffunktion aufzurufen. Sie können diesen Dateideskriptor zum späteren Lesen verwenden zu dieser bereits geöffneten Datei:
fs.open('/path/to/file', 'r', function(err, fd) {
// fd-Dateideskriptor erhalten
});
Der erste Parameter von fs.open ist der Dateipfad und der zweite Parameter sind einige Tags, die den Modus angeben, in dem die Datei geöffnet werden soll. Diese Tags können r, r, w, w, a oder a sein. Nachfolgend finden Sie eine Beschreibung dieser Tags (von der fopen-Seite der UNIX-Dokumentation)
1.r – Öffnen Sie die Datei im schreibgeschützten Modus. Die Anfangsposition des Datenstroms befindet sich am Anfang der Datei
2.r – Öffnen Sie die Datei im Lese-/Schreibmodus. Die Anfangsposition des Datenstroms befindet sich am Anfang der Datei
3.w – Wenn die Datei vorhanden ist, wird die Dateilänge auf 0 gelöscht, dh der Dateiinhalt geht verloren. Wenn es nicht existiert, versuchen Sie es zu erstellen. Die Anfangsposition des Datenstroms liegt am Anfang der Datei
4.w – Öffnen Sie die Datei im Lese-/Schreibmodus. Wenn die Datei nicht vorhanden ist, setzen Sie die Dateilänge auf 0, d. h. der Dateiinhalt geht verloren. Die Anfangsposition des Datenstroms liegt am Anfang der Datei
5.a – Öffnen Sie die Datei im Nur-Schreibmodus. Wenn die Datei nicht vorhanden ist, wird die Anfangsposition des Datenstroms am Ende der Datei angehängt Ende der Datei.
6.a – Öffnen Sie die Datei zum Lesen und Schreiben. Versuchen Sie, sie zu erstellen. Bei jedem weiteren Schreibvorgang werden die Daten an das Ende angehängt der Datei.
Datei lesen
Sobald die Datei geöffnet ist, können Sie mit dem Lesen des Dateiinhalts beginnen. Bevor Sie beginnen, müssen Sie jedoch zunächst einen Puffer erstellen, um die Daten zu platzieren. Dieses Pufferobjekt wird als Parameter an die Funktion fs.read übergeben und von fs.read mit Daten gefüllt.
fs.open('./my_file.txt', 'r', function open(err, fd) {
if (err) { throw err }
var readBuffer = new Buffer(1024),
bufferOffset = 0,
bufferLength = readBuffer.length,
filePosition = 100;
fs.read(fd,
readBuffer,
bufferOffset,
bufferLength,
filePosition,
Funktion read(err, readBytes) {
if (err) { throw err; }
console.log('just read ' readBytes ' bytes');
if (readBytes > 0) {
console.log(readBuffer.slice(0, readBytes));
}
});
});
上面代码尝试打开一个文件,当成功打开后(调用opened函数),开始请求从文件流第100个字节开始读取随后1024个字节的数据(第11行).
fs.read()1.有错误发生
2.成功读取了数据
3.没有数据可读
如果有错误发生,第一个参数(err)会为回调函数提供一个包含错误信息的对象,否则这个参数为null.如果成功读取了数据, 第二个参数(readBytes)会指明被读到缓冲区里数据的大小,如果值是0,则表示到达了文件末尾.
注意:一旦把缓冲区对象传递给fs.open(),缓冲对象的控制权就转移给给了read命令,只有当回调函数被调用,缓冲区对象的控制权才会回到你手里.因此在这之前,不要读写或者让其它函数调用使用这个缓冲区对象;否则,你可能会读到不完整的数据, 更糟的情况是, 你可能会并发地往这个缓冲区对象里写数据.
写文件通过传递给fs.write()传递一个包含数据的缓冲对象,来往一个已打开的文件里写数据:
if (err) { throw err; }
var writeBuffer = new Buffer('write this string'),
bufferPosition = 0,
bufferLength = writeBuffer.length, filePosition = null;
fs.write( fd,
writeBuffer,
bufferPosition,
bufferLength,
filePosition,
Funktion geschrieben(ähm, geschrieben) {
if (err) { throw err; }
console.log('schrieb ' geschriebene ' Bytes');
});
});
In diesem Beispiel versucht die 2. Codezeile (Anmerkung des Übersetzers: Originaltext ist 3), eine Datei im Anhängemodus (a) zu öffnen, und dann schreibt die 7. Codezeile (Anmerkung des Übersetzers: Originaltext ist 9). Daten in die Datei. Dem Pufferobjekt müssen mehrere Informationen als Parameter beigefügt werden:
1. Pufferdaten
2. Wo beginnen die zu schreibenden Daten im Puffer
?
3. Die Länge der zu schreibenden Daten
4. An welche Stelle in der Datei werden die Daten geschrieben?
5. Die Rückruffunktion write
In diesem Beispiel ist der filePostion-Parameter null, was bedeutet, dass die Schreibfunktion die Daten an die aktuelle Position des Dateizeigers schreibt. Da die Datei im Anhängemodus geöffnet wird, befindet sich der Dateizeiger am Ende Datei.
Verwenden Sie, ähnlich wie beim Lesevorgang, niemals das eingehende Pufferobjekt während der Ausführung von fs.write. Sobald fs.write mit der Ausführung beginnt, übernimmt es die Kontrolle über dieses Pufferobjekt. Sie können nur warten, bis die Callback-Funktion aufgerufen wird, bevor Sie sie erneut verwenden können.
Datei schließen
Vielleicht ist Ihnen aufgefallen, dass bisher keines der Beispiele in diesem Kapitel Code zum Schließen der Datei enthält. Da es sich um kleine und einfache Beispiele handelt, die nur einmal verwendet werden, stellt das Betriebssystem sicher, dass alle Dateien geschlossen werden, wenn der Node-Prozess endet.
In einer echten Anwendung möchten Sie jedoch sicherstellen, dass Sie eine Datei, sobald Sie sie öffnen, auch schließen. Dazu müssen Sie alle offenen Dateideskriptoren im Auge behalten und sie schließlich durch den Aufruf von fs.close(fd[,callback]) schließen, wenn sie nicht mehr verwendet werden. Es ist leicht, einen Dateideskriptor zu übersehen, wenn Sie nicht aufpassen. Das folgende Beispiel stellt eine Funktion namens openAndWriteToSystemLog bereit, die zeigt, wie die Datei sorgfältig geschlossen wird:
Hier wird eine Funktion namens openAndWriteToSystemLog bereitgestellt, die ein Pufferobjekt akzeptiert, das die zu schreibenden Daten enthält, und eine Rückruffunktion, die aufgerufen wird, nachdem der Vorgang abgeschlossen ist oder ein Fehler auftritt, die erste Rückruffunktion Die Parameter enthalten das Fehlerobjekt.
Achten Sie auf die interne Funktion notifyError, die die Datei schließt und den aufgetretenen Fehler meldet.
Hinweis: An diesem Punkt wissen Sie, wie Sie atomare Operationen auf niedriger Ebene zum Öffnen, Lesen, Schreiben und Schließen von Dateien verwenden. Allerdings verfügt Node auch über eine Reihe fortgeschrittenerer Konstruktoren, die es Ihnen ermöglichen, einfacher mit Dateien zu arbeiten.
Wenn Sie beispielsweise auf sichere Weise zwei oder mehr Schreibvorgängen das gleichzeitige Anhängen von Daten an eine Datei ermöglichen möchten, können Sie WriteStream verwenden.
Wenn Sie außerdem einen bestimmten Bereich einer Datei lesen möchten, sollten Sie die Verwendung von ReadStream in Betracht ziehen. Diese beiden Anwendungsfälle werden in Kapitel 9 „Streams zum Lesen und Schreiben von Daten“ vorgestellt.
Zusammenfassung
Wenn Sie Dateien verwenden, müssen Sie in den meisten Fällen Dateipfadinformationen verarbeiten und extrahieren. Mit dem Pfadmodul können Sie Pfade verketten, Pfade standardisieren, Pfadunterschiede berechnen und relative Pfade in absolute Pfade umwandeln. Sie können die Erweiterung, den Dateinamen, das Verzeichnis und andere Pfadkomponenten des angegebenen Dateipfads extrahieren.
Knoten stellt im fs-Modul eine Reihe von Low-Level-APIs für den Zugriff auf das Dateisystem bereit. Die Low-Level-APIs verwenden Dateideskriptoren, um Dateien zu bedienen. Sie können eine Datei mit fs.open öffnen, mit fs.write in eine Datei schreiben, mit fs.read eine Datei lesen und mit fs.close eine Datei schließen.
Sie sollten immer die richtige Fehlerbehandlungslogik verwenden, um Dateien zu schließen, wenn ein Fehler auftritt. Stellen Sie dabei sicher, dass offene Dateideskriptoren geschlossen werden, bevor der Aufruf zurückkehrt.