亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

Node.js文件操作詳解

2019-11-20 14:15:59
字體:
來源:轉載
供稿:網友

Node有一組數據流API,可以像處理網絡流那樣處理文件,用起來很方便,但是它只允許順序處理文件,不能隨機讀寫文件。因此,需要使用一些更底層的文件系統操作。

本章覆蓋了文件處理的基礎知識,包括如何打開文件,讀取文件某一部分,寫數據,以及關閉文件。

Node的很多文件API幾乎是UNIX(POSIX)中對應文件API 的翻版,比如使用文件描述符的方式,就像UNIX里一樣,文件描述符在Node里也是一個整型數字,代表一個實體在進程文件描述符表里的索引。

有3個特殊的文件描述符――1,2和3。他們分別代表標準輸入,標準輸出和標準錯誤文件描述符。標準輸入,顧名思義,是個只讀流,進程用它來從控制臺或者進程通道讀取數據。標準輸出和標準錯誤是僅用來輸出數據的文件描述符,他們經常被用來向控制臺,其它進程或文件輸出數據。標準錯誤負責錯誤信息輸出,而標準輸出負責普通的進程輸出。

一旦進程啟動完畢,就能使用這幾個文件描述符了,它們其實并不存在對應的物理文件。你不能讀寫某個隨機位置的數據,(譯者注:原文是You can write to and read from specific positions within the file.根據上下文,作者可能少寫了個“not”),只能像操作網絡數據流那樣順序的讀取和輸出,已寫入的數據就不能再修改了。

普通文件不受這種限制,比如Node里,你即可以創建只能向尾部追加數據的文件,還可以創建讀寫隨機位置的文件。

幾乎所有跟文件相關的操作都會涉及到處理文件路徑,本章先會將介紹這些工具函數,然后再深入講解文件讀寫和數據操作

處理文件路徑

文件路徑分為相對路徑和絕對路徑兩種,用它們來表示具體的文件。你可以合并文件路徑,可以提取文件名信息,甚至可以檢測文件是否存在。

Node里,可以用字符串來操處理文件路徑,但是那樣會使問題變復雜,比如你要連接路徑的不同部分,有些部分以 “/”結尾有些卻沒有,而且路徑分割符在不同操作系統里也可能會不一樣,所以,當你連接它們時,代碼就會非常羅嗦和麻煩。

幸運的是,Node有個叫path的模塊,可以幫你標準化,連接,解析路徑,從絕對路徑轉換到相對路徑,從路徑中提取各部分信息,檢測文件是否存在。總的來說,path模塊其實只是些字符串處理,而且也不會到文件系統去做驗證(path.exists函數例外)。

路徑的標準化

在存儲或使用路徑之前將它們標準化通常是個好主意。比如,由用戶輸入或者配置文件獲得的文件路徑,或者由兩個或多個路徑連接起來的路徑,一般都應該被標準化??梢杂胮ath模塊的normalize函數來標準化一個路徑,而且它還能處理“..”,“.”“//”。比如:

復制代碼 代碼如下:

var path = require('path');

path.normalize('/foo/bar//baz/asdf/quux/..');

// => '/foo/bar/baz/asdf'

連接路徑

使用path.join()函數,可以連接任意多個路徑字符串,只用把所有路徑字符串依次傳遞給join()函數就可以:

復制代碼 代碼如下:

                   var path = require('path');

                   path.join('/foo', 'bar', 'baz/asdf', 'quux', '..');

                   // => '/foo/bar/baz/asdf'

如你所見,path.join()內部會自動將路徑標準化。

解析路徑

用path.resolve()可以把多個路徑解析為一個絕對路徑。它的功能就像對這些路徑挨個不斷進行“cd”操作,和cd命令的參數不同,這些路徑可以是文件,并且它們不必真實存在――path.resolve()方法不會去訪問底層文件系統來確定路徑是否存在,它只是一些字符串操作。

比如:

復制代碼 代碼如下:

                   var path = require('path');

                   path.resolve('/foo/bar', './baz');

                   // => /foo/bar/baz

                   path.resolve('/foo/bar', '/tmp/file/');

                   // => /tmp/file

如果解析結果不是絕對路徑,path.resolve()會把當前工作目錄作為路徑附加到解析結果前面,比如:

復制代碼 代碼如下:

        path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
        // 如果當前工作目錄是/home/myself/node, 將返回
        // => /home/myself/node/wwwroot/static_files/gif/image.gif'

計算兩個絕對路徑的相對路徑

path.relative()可以告訴你如果從一個絕對地址跳轉到另外一個絕對地址,比如:

復制代碼 代碼如下:

                var path = require('path');

                path.relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb');

                // => ../../impl/bbb

從路徑提取數據

以路徑“/foo/bar/myfile.txt”為例,如果你想獲取父目錄(/foo/bar)的所有內容,或者讀取同級目錄的其它文件,為此,你必須用path.dirname(filePath)獲得文件路徑的目錄部分,比如:

復制代碼 代碼如下:

                   var path = require('path');

                   path.dirname('/foo/bar/baz/asdf/quux.txt');

                   // => /foo/bar/baz/asdf

 或者,你想從文件路徑里得到文件名,也就是文件路徑的最后那一部分,可以使用path.basename函數:
 

復制代碼 代碼如下:

                    var path = require('path');

                   path.basename('/foo/bar/baz/asdf/quux.html')

                   // => quux.html
 

文件路徑里可能還包含文件擴展名,通常是文件名中最后一個“.”字符之后的那部分字符串。

path.basename還可以接受一個擴展名字符串作為第二個參數,這樣返回的文件名就會自動去掉擴展名,僅僅返回文件的名稱部分:

復制代碼 代碼如下:

                   var path = require('path');

                   path.basename('/foo/bar/baz/asdf/quux.html', '.html');

                   // => quux

 要想這么做你首先還得知道文件的擴展名,可以用path.extname()來獲取擴展名:

復制代碼 代碼如下:

                   var path = require('path');

                   path.extname('/a/b/index.html');

                   // => '.html'

                   path.extname('/a/b.c/index');

                   // => ''

                   path.extname('/a/b.c/.');

                   // => ''

                   path.extname('/a/b.c/d.');

                   // => '.'

檢查路徑是否存在

目前為止,前面涉及到的路徑處理操作都跟底層文件系統無關,只是一些字符串操作。然而,有些時候你需要判斷一個文件路徑是否存在,比如,你有時候需要判斷文件或目錄是否存在,如果不存在的話才創建它,可以用path.exsits():

復制代碼 代碼如下:

                   var path = require('path');

                   path.exists('/etc/passwd', function(exists) {

                            console.log('exists:', exists);

                            // => true

                   });

                   path.exists('/does_not_exist', function(exists) {

                            console.log('exists:', exists);

                            // => false

                   });

注意:從Node0.8版本開始,exists從path模塊移到了fs模塊,變成了fs.exists,除了命名空間不同,其它都沒變:

復制代碼 代碼如下:

                   var fs = require('fs');

                   fs.exists('/does_not_exist', function(exists) {

                            console.log('exists:', exists);

                            // => false

                   });

path.exists()是個I/O操作,因為它是異步的,因此需要一個回調函數,當I/O操作返回后調用這個回調函數,并把結果傳遞給它。你還可以使用它的同步版本path.existsSync(),功能完全一樣,只是它不會調用回調函數,而是直接返回結果:

復制代碼 代碼如下:

                  var path = require('path');

                 path.existsSync('/etc/passwd');

                 // => true

fs模塊介紹

fs模塊包含所有文件查詢和處理的相關函數,用這些函數,可以查詢文件信息,讀寫和關閉文件。這樣導入fs模塊:

復制代碼 代碼如下:

         var fs = require(‘fs')

查詢文件信息

有時你可能需要知道文件的大小,創建日期或者權限等文件信息,可以使用fs.stath函數來查詢文件或目錄的元信息:

復制代碼 代碼如下:

                   var fs = require('fs');

                  fs.stat('/etc/passwd', function(err, stats) {

                                    if (err) { throw err;}

                                    console.log(stats);

                  });

這塊代碼片斷會有類似下面的輸出

復制代碼 代碼如下:

 { dev: 234881026,

ino: 95028917,

mode: 33188,

nlink: 1,

uid: 0,

gid: 0,

rdev: 0,

size: 5086,

blksize: 4096,

blocks: 0,

atime: Fri, 18 Nov 2011 22:44:47 GMT,

mtime: Thu, 08 Sep 2011 23:50:04 GMT,

ctime: Thu, 08 Sep 2011 23:50:04 GMT }

1.fs.stat()調用會將一個stats類的實例作為參數傳遞給它的回調函數,可以像下面這樣使用stats實例:

2.stats.isFile() ―― 如果是個標準文件,而不是目錄,socket,符號鏈接或者設備,則返回true,否則false
3.stats.isDiretory() ―― 如果是目錄則返回tue,否則false
4.stats.isBlockDevice() ―― 如果是塊設備則返回true,在大多數UNIX系統中塊設備通常都在/dev目錄下
5.stats.isChracterDevice() ―― 如果是字符設備返回true
6.stats.isSymbolickLink() ―― 如果是文件鏈接返回true
7.stats.isFifo() ―― 如果是個FIFO(UNIX命名管道的一個特殊類型)返回true
8.stats.isSocket() ―― 如果是個UNIX socket(TODO:googe it)

打開文件

在讀取或處理文件之前,必須先使用fs.open函數打開文件,然后你提供的回調函數會被調用,并得到這個文件的描述符,稍后你可以用這個文件描述符來讀寫這個已經打開的文件:

復制代碼 代碼如下:

                   var fs = require('fs');

                   fs.open('/path/to/file', 'r', function(err, fd) {

                        // got fd file descriptor

                   });

fs.open的第一個參數是文件路徑,第二個參數是一些用來指示以什么模式打開文件的標記,這些標記可以是r,r+,w,w+,a或者a+。下面是這些標記的說明(來自UNIX文檔的fopen頁)

1.r ―― 以只讀方式打開文件,數據流的初始位置在文件開始
2.r+ ―― 以可讀寫方式打開文件,數據流的初始位置在文件開始
3.w ――如果文件存在,則將文件長度清0,即該文件內容會丟失。如果不存在,則嘗試創建它。數據流的初始位置在文件開始
4.w+ ―― 以可讀寫方式打開文件,如果文件不存在,則嘗試創建它,如果文件存在,則將文件長度清0,即該文件內容會丟失。數據流的初始位置在文件開始
5.a ―― 以只寫方式打開文件,如果文件不存在,則嘗試創建它,數據流的初始位置在文件末尾,隨后的每次寫操作都會將數據追加到文件后面。
6.a+ ――以可讀寫方式打開文件,如果文件不存在,則嘗試創建它,數據流的初始位置在文件末尾,隨后的每次寫操作都會將數據追加到文件后面。

讀文件

一旦打開了文件,就可以開始讀取文件內容,但是在開始之前,你得先創建一個緩沖區(buffer)來放置這些數據。這個緩沖區對象將會以參數形式傳遞給fs.read函數,并被fs.read填充上數據。

復制代碼 代碼如下:

var fs = require('fs');

fs.open('./my_file.txt', 'r', function opened(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,

         function 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()的最后一個參數是個回調函數(第16行),當下面三種情況發生時,它會被調用:

1.有錯誤發生
2.成功讀取了數據
3.沒有數據可讀

如果有錯誤發生,第一個參數(err)會為回調函數提供一個包含錯誤信息的對象,否則這個參數為null。如果成功讀取了數據,第二個參數(readBytes)會指明被讀到緩沖區里數據的大小,如果值是0,則表示到達了文件末尾。

注意:一旦把緩沖區對象傳遞給fs.open(),緩沖對象的控制權就轉移給給了read命令,只有當回調函數被調用,緩沖區對象的控制權才會回到你手里。因此在這之前,不要讀寫或者讓其它函數調用使用這個緩沖區對象;否則,你可能會讀到不完整的數據,更糟的情況是,你可能會并發地往這個緩沖區對象里寫數據。

寫文件

通過傳遞給fs.write()傳遞一個包含數據的緩沖對象,來往一個已打開的文件里寫數據:

復制代碼 代碼如下:

var fs = require('fs');

fs.open('./my_file.txt', 'a', function opened(err, fd) {

    if (err) { throw err; }

    var writeBuffer = new Buffer('writing this string'),

    bufferPosition = 0,

    bufferLength = writeBuffer.length, filePosition = null;

    fs.write( fd,

        writeBuffer,

        bufferPosition,

        bufferLength,

        filePosition,

        function wrote(err, written) {

           if (err) { throw err; }

           console.log('wrote ' + written + ' bytes');

        });

});

這個例子里,第2(譯者注:原文為3)行代碼嘗試用追加模式(a)打開一個文件,然后第7行代碼(譯者注:原文為9)向文件寫入數據。緩沖區對象需要附帶幾個信息一起做為參數:

1.緩沖區的數據
2.待寫數據從緩沖區的什么位置開始
3.待寫數據的長度
4.數據寫到文件的哪個位置
5.當操作結束后被調用的回調函數wrote

這個例子里,filePostion參數為null,也就是說write函數將會把數據寫到文件指針當前所在的位置,因為是以追加模式打開的文件,因此文件指針在文件末尾。

跟read操作一樣,千萬不要在fs.write執行過程中使用哪個傳入的緩沖區對象,一旦fs.write開始執行它就獲得了那個緩沖區對象的控制權。你只能等到回調函數被調用后才能再重新使用它。

關閉文件

你可能注意到了,到目前為止,本章的所有例子都沒有關閉文件的代碼。因為它們只是些僅使用一次而且又小又簡單的例子,當Node進程結束時,操作系統會確保關閉所有文件。

但是,在實際的應用程序中,一旦打開一個文件你要確保最終關閉它。要做到這一點,你需要追蹤所有那些已打開的文件描述符,然后在不再使用它們的時候調用fs.close(fd[,callback])來最終關閉它們。如果你不仔細的話,很容易就會遺漏某個文件描述符。下面的例子提供了一個叫openAndWriteToSystemLog的函數,展示了如何小心的關閉文件:

復制代碼 代碼如下:

var fs = require('fs');
function openAndWriteToSystemLog(writeBuffer, callback){
    fs.open('./my_file', 'a', function opened(err, fd) {
        if (err) { return callback(err); }
        function notifyError(err) {
            fs.close(fd, function() {
                callback(err);
            });
        }
        var bufferOffset = 0,
        bufferLength = writeBuffer.length,
        filePosition = null;
        fs.write( fd, writeBuffer, bufferOffset, bufferLength, filePosition,
            function wrote(err, written) {
                if (err) { return notifyError(err); }
                fs.close(fd, function() {
                    callback(err);
                });
            }
        );
    });
}
openAndWriteToSystemLog(
    new Buffer('writing this string'),
    function done(err) {
        if (err) {
            console.log("error while opening and writing:", err.message);
            return;
        }
        console.log('All done with no errors');
    }
);

  在這兒,提供了一個叫openAndWriteToSystemLog的函數,它接受一個包含待寫數據的緩沖區對象,以及一個操作完成或者出錯后被調用的回調函數,如果有錯誤發生,回調函數的第一個參數會包含這個錯誤對象。

注意那個內部函數notifyError,它會關閉文件,并報告發生的錯誤。

注意:到此為止,你知道了如何使用底層的原子操作來打開,讀,寫和關閉文件。然而,Node還有一組更高級的構造函數,允許你用更簡單的方式來處理文件。

比如,你想用一種安全的方式,讓兩個或者多個write操作并發的往一個文件里追加數據,這時你可以使用WriteStream。

還有,如果你想讀取一個文件的某個區域,可以考慮使用ReadStream。這兩種用例會在第九章“數據的讀,寫流”里介紹。

小結

當你使用文件時,多數情況下都需要處理和提取文件路徑信息,通過使用path模塊你可以連接路徑,標準化路徑,計算路徑的差別,以及將相對路徑轉化成絕對路徑。你可以提取指定文件路徑的擴展名,文件名,目錄等路徑組件。

Node在fs模塊里提供了一套底層API來訪問文件系統,底層API使用文件描述符來操作文件。你可以用fs.open打開文件,用fs.write寫文件,用fs.read讀文件,并用fs.close關閉文件。

當有錯誤發生時,你應該總是使用正確的錯誤處理邏輯來關閉文件――以確保在調用返回前關閉那些已打開的文件描述符。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久99久久99精品中文字幕| 亚洲图片欧美午夜| 亚洲精品国产精品乱码不99按摩| 欧美成人性色生活仑片| 国产欧美久久一区二区| 亚洲精品久久久久久下一站| 色伦专区97中文字幕| 久久亚洲欧美日韩精品专区| 亚洲国产精品久久久| 一区二区亚洲精品国产| 亚洲激情视频在线| 亚洲国产精品网站| 91精品视频免费| 欧美一级电影久久| 亚洲色图50p| 欧美日韩一区二区三区在线免费观看| 国产精品永久免费| 欧美一区在线直播| 91久久久亚洲精品| 一区二区三区天堂av| 日韩精品免费综合视频在线播放| 黄色成人在线播放| 日本欧美一二三区| 欧美日韩精品在线观看| 中文字幕日韩在线播放| 欧美性猛交xxxx免费看| 国产z一区二区三区| 精品高清美女精品国产区| 97国产成人精品视频| 亚洲精品一区av在线播放| 国产日韩在线精品av| 91免费看国产| 欧美日本精品在线| 国产精品露脸av在线| 日韩在线免费av| 日韩欧美亚洲国产一区| 亚洲大胆人体视频| 欧美黄网免费在线观看| 久久久久久91香蕉国产| 中文字幕亚洲一区| 欧美日韩在线视频首页| 青青草成人在线| 一本一道久久a久久精品逆3p| 欧美日韩亚洲网| 亚洲第一精品福利| 亚洲综合日韩中文字幕v在线| 日韩电影网在线| 欧美高清视频在线| 91精品视频播放| 中文字幕欧美在线| 国产日韩亚洲欧美| 中文字幕av一区中文字幕天堂| 国产精品69精品一区二区三区| 久久久久久成人精品| 尤物yw午夜国产精品视频| 在线亚洲国产精品网| 一区二区三区在线播放欧美| 色诱女教师一区二区三区| 欧美午夜xxx| 亚洲а∨天堂久久精品9966| 国产精品偷伦视频免费观看国产| 欧美日韩视频在线| 91精品在线影院| 91大神福利视频在线| 日韩免费观看av| 亚洲美女喷白浆| 欧美多人爱爱视频网站| 欧美中文字幕在线| 欧美性猛交视频| 中文字幕亚洲综合久久筱田步美| 国产欧美日韩免费看aⅴ视频| 国内外成人免费激情在线视频网站| 在线观看欧美www| 91麻豆国产语对白在线观看| 亚洲欧美国产精品久久久久久久| 成人av.网址在线网站| 91在线视频成人| 亚洲国产精久久久久久| 亚洲国产又黄又爽女人高潮的| 日韩福利视频在线观看| 性欧美亚洲xxxx乳在线观看| 综合网中文字幕| 欧美视频裸体精品| 亚洲一区二区三区sesese| 久久91亚洲人成电影网站| 91av视频在线免费观看| 精品视频在线导航| 日韩中文字幕不卡视频| 中文字幕在线观看亚洲| 国产不卡av在线免费观看| 欧美综合国产精品久久丁香| 日本欧美爱爱爱| 久久天天躁夜夜躁狠狠躁2022| 欧美日产国产成人免费图片| 国产主播欧美精品| 国产午夜精品全部视频在线播放| 黑人精品xxx一区| 97精品一区二区视频在线观看| 成人精品久久久| 久久九九精品99国产精品| 久久精视频免费在线久久完整在线看| 一区二区在线视频播放| 久久久久久噜噜噜久久久精品| 日韩中文理论片| 欧美性资源免费| 欧美一级电影免费在线观看| 国模私拍一区二区三区| 日韩一二三在线视频播| 久久精品国产69国产精品亚洲| 日韩成人黄色av| 久久久久久久国产精品| 国内精品国产三级国产在线专| 92福利视频午夜1000合集在线观看| 亚洲欧洲成视频免费观看| 欧美丰满少妇xxxxx| 久久久精品国产亚洲| 亚洲人精选亚洲人成在线| 午夜精品久久久久久久白皮肤| 国产精品无码专区在线观看| 国产精品美女久久久免费| 亚洲欧洲高清在线| 欧美疯狂做受xxxx高潮| 国产91精品在线播放| 亚洲精品国产美女| 国产精品美女久久| 欧美色欧美亚洲高清在线视频| 91av视频在线免费观看| 亚洲女成人图区| 欧美在线精品免播放器视频| 亚洲精品日产aⅴ| 日韩av电影手机在线| 最近更新的2019中文字幕| 狠狠久久亚洲欧美专区| 亚洲已满18点击进入在线看片| 久久精品视频在线观看| 国产精品成人免费视频| 国产精自产拍久久久久久| 人人做人人澡人人爽欧美| 久久亚洲影音av资源网| 日韩欧美中文字幕在线观看| 欧美成人激情图片网| 国产精品专区第二| 日韩欧美国产成人| 欧美激情xxxxx| 国产精品91免费在线| 中文字幕一区日韩电影| 亚洲男人天堂视频| 亚洲一二三在线| 国产福利精品在线| 欧美黑人狂野猛交老妇| 91精品国产亚洲| 亚洲欧美日韩国产中文| 国产+成+人+亚洲欧洲| 福利二区91精品bt7086| 中文国产成人精品| 中文字幕精品一区二区精品| 韩国福利视频一区| 91精品国产自产在线| 欧美肥婆姓交大片| 国产精品96久久久久久又黄又硬| 美乳少妇欧美精品| 国产精品视频男人的天堂| 日韩免费黄色av| 国产精品国产福利国产秒拍|