站長資訊網
        最全最豐富的資訊網站

        nodejs的哪個模塊可以操作文件

        nodejs的“文件系統”模塊可以操作文件。fs(文件系統)模塊是nodejs提供的用于訪問本地文件系統的功能模塊,使用fs模塊可以實現文件及目錄的創建,寫入、刪除等操作。

        nodejs的哪個模塊可以操作文件

        本教程操作環境:windows7系統、nodejs 12.19.0版、Dell G3電腦。

        一、fs模塊簡介

        1. 基本概念

        fs(file system)模塊是nodejs提供的用于訪問本地文件系統的功能模塊,它使得運行于nodejs環境下的JavaScript具備直接讀寫本地文件的能力。

        fs模塊是nodejs的核心模塊之一,只要安裝了nodejs,就可以直接使用,不需要單獨安裝。引入fs模塊非常簡單:

        let fs = require('fs');

        接下來就可以調用fs模塊的相關接口直接讀寫文件系統。

        fs模塊主要提供了以下的接口類:

        • fs.Dir,目錄類。可理解為文件夾。

        • fs.Dirent,目錄項類。通過Dir類的返回值獲得,表示該目錄下的一個子項,可能是文件或子目錄。

        • fs.FSWatcher,文件監聽類,它可以為一個文件創建一個監聽器,當文件變化時觸發回調。

        • fs.StatWatcher,調用fs.watchFile()方法之后的返回值類型,主要用于協助控制事件循環。

        • fs.ReadStream,讀取流。當流式讀取文件時需要使用該類。

        • fs.Stats,文件元信息類。通過該類可以獲取文件相關信息(如文件類型、文件大小、文件描述符等)。

        • fs.WriteStream,寫入流。當流式寫入數據時需要使用。

        除了以上的類,fs模塊還提供了非常多的實例方法,它們可以直接通過fs調用,如讀取文件的方法reafFile

        let fs = require('fs');  fs.readFile('./README.md', function(err, data) = {   if (err) throw err;   ... // 處理數據 })

        該函數以異步的方式讀取文件,以可能拋出的異常作為回調函數的第一個參數(這樣設計的目的是“強制”或者“提醒”開發者去處理可能出現的異常),而真正的文檔數據則作為第二個參數。

        fs模塊中幾乎所有默認的讀寫函數都是異步的,不過它也同時提供了這些函數的同步版本,一般是在函數后面加Sync。如上面的代碼還可以用readFileSync改寫:

        let fs = require('fs');  try {   let data = fs.readFileSync('./README.md', 'utf8'); } catch(e = {   console.error(e); })

        大多數情況下,nodejs推薦使用異步版本的讀寫函數來提升系統性能。而如果要使用同步版本的函數,應該盡可能使用try catch捕獲異常,以防止讀寫失敗造成主線程崩潰。

        2. 文件路徑

        既然是文件系統模塊,就必然要根據文件路徑找到需要操作的文件。fs模塊支持的路徑類型分為三類:字符串BufferURL對象

        (1). 字符串

        字符串類是最常用的路徑類型,包括絕對路徑相對路徑。絕對路徑指的是相對于文件系統根目錄的路徑,而相對路徑是相對于當前工作目錄的路徑(即運行node命令時所在的目錄,可以通過process.cwd()獲取到)。

        當使用絕對路徑時,windows和其他操作系統存在一定差異。因為在其他大多數操作系統中,驅動器只有唯一的根目錄;而在windows上,則存在多個獨立的驅動盤(如C盤、D盤等)。從寫法上來看,絕對路徑一般是以/開頭,表示驅動盤的根目錄:

        fs.readFile('/README.md', (err, data) => {   ... });

        而在windows上,則是以驅動盤開頭的:

        fs.readFile('d://nodejs/README.md', (err, data) => {   ... })

        我們知道,一般windows都是使用反斜線作為路徑分隔符的。不過在nodejs中作了兼容,使用斜線或者反斜線都可以正確識別,因此下面的寫法也是正確的:

        fs.readFile('d:\nodejsREADME.md', (err, data) => {   ... })

        需要注意的是,當使用fs.readdir('c:')讀取C盤目錄時,實際上讀取的是c:\userxxx這個用戶根目錄,必須加雙斜線或雙反斜線,才可以讀取到真正的c盤根目錄:fs.readdir('c://')

        而相對路徑則是相對于當前工作目錄的路徑,通常以../(回退到上級目錄)、./(當前目錄)或當前路徑下的某個目錄項開頭。如:

        // 當前目錄的上一級目錄下的README.md文件 fs.readFileSync('../README.md');  // 當前目錄下nodejs文件夾內的README.md fs.readFileSync('./nodejs/README.md'); fs.readFileSync('nodejs/README.md');

        ./開頭表示當前目錄,可以直接省略。

        (2). Buffer

        Buffer類型的路徑和字符串幾乎沒有差別,只是某些操作系統將文件路徑視為二進制字節序列,因此通常需要用Buffer.from('README.md')的方式將字符串路徑轉化為二進制字節序列。由于這類操作系統比較少用,這里不再詳細介紹。

        (3). URL

        URL(uniform resource locator,統一資源定位器)是一種通用的資源定位方案,它將任意資源(包括本地文件)都視為網絡資源,然后以一種統一的方式定位它們。

        fs模塊僅支持使用file協議的URL對象,該協議的前綴為file:///,路徑格式如下:

        // 在windows下,定位某個主機上的文件, // 一般用于讀寫局域網內的共享文件夾 fs.readFileSync(new URL('file:///主機名/README.md'));  // 定位本地資源 fs.readFileSync(new URL('file:///c:/README.md'));

        以上兩種寫法都是針對windows平臺的,第一種主要用于局域網內的共享文件讀取,第二種用于讀寫本地文件。

        對于其他平臺,url的格式如下:

        fs.readFileSync(new URL('file:///nodejs/README.md'));

        它會被轉化為絕對路徑:/nodejs/README.md。URL類型的路徑不支持相對路徑。

        二、fs模塊的常用類

        鑒于fs模塊的接口數量較為龐大,這里暫不一一探討,如果感興趣,請參考 nodejs中文網 – fs模塊。本文主要是介紹以下幾個接口類中較為常用的一些api,基本上可以滿足大部分文件系統操作的需求:

        • fs.Dir

        • fs.Dirent

        • fs.ReadStream

        • fs.Stats

        • fs.WriteStream

        1. fs.Dir(v12.12)

        這是fs模塊對目錄(或稱為文件夾)的抽象類。調用fs.opendir()fs.opendirSync()fsPromises.opendir()這三個方法時會返回一個Dir類型的對象,用于操作打開后的目錄。

        比如我們要遍歷當前文件夾下的所有子目錄及文件,可以用下面的代碼(v12.12以上版本可用):

        async function getChild (path) {   // 讀取目錄子項,返回一個promise   let dir = await fs.promises.opendir(path);    for await (let dirent of dir) {     console.log(dirent.name);   }}

        關閉目錄有三個方法:dir.close()dir.close(callback)dir.closeSync()

        dir.close()返回的是一個promise,向其注冊then方法可以在目錄關閉后執行回調函數,如dir.close().then((e) => { ... })dir.close(callback)是關閉目錄的異步方法,直接傳入回調函數,它會在文件關閉后調用;dir.closeSync()是關閉目錄的同步方法,只有目錄成功關閉后才會執行后續代碼。

        dir.path的值為當前目錄的路徑,即調用opendir方法時傳入的路徑。

        讀取該目錄的目錄項的三個方法為:dir.read()dir.read(callback)dir.readSync()

        dir.read()返回的是一個promise數組或null,分別負責讀取每一個目錄項,主要用于async函數遍歷:

        async function read (path) {   let dir = await fs.promises.opendir(path);   for await (let dirent of dir.read()){     ...   }}

        dir.read(callback)則是讀取目錄項的異步版本,每次讀取到一個子項,就會調用一次callback;dir.readSync()是讀取目錄項的同步版本,返回的是Dirent類型的數組或null。

        2. fs.Dirent(v10.10)

        目錄項類。當通過fs.opendir()方法讀取一個目錄時,它的每一個子項就是一個Dirent類對象,每個Dirent對象可能是一個子目錄,或者是一個文件。每個Dirent對象都是文件名和文件類型組成的。

        fs.Dirent提供的方法主要是用于判斷目錄項類型:

        • dirent.isBlockDevice(),是否為塊設備。

        • dirent.isCharacterDevice(),是否為字符設備。

        • dirent.isDirectory(),是否為系統目錄。

        • dirent.isFIFO(),是否為先入先出通道。

        • dirent.isFile(),是否為普通文件。

        • dirent.isSocket(),是否為套接字。

        • dirent.isSymbolicLink(),是否為符號鏈接,即快捷方式。

        另外,dirent.name的值為目錄項的名字。

        3. fs.ReadStream

        讀取流類,用于流式讀取文件。該類由fs.createReadStream()創建并返回。

        fs.ReadStream支持三個事件:

        • close,讀取流關閉事件。

        • open,讀取流打開事件。

        • ready,讀取流就緒事件,在open事件發生后立即觸發。

        回調函數接收文件描述符fd作為參數,用于對文件的后續操作。

        fs.ReadStream實例有三個實例屬性:

        • readStream.bytesRead,已讀取的字節數。

        • readStream.path,文件路徑。

        • readStream.pending,文件是否就緒(ready事件發生前,該值為true,發生后變為false)。

        4. fs.Stats

        提供對文件信息的描述。調用fs.stat()fs.lstat()fs.fstat()會返回該類型的對象。

        一個fs.Stats實例包含以下屬性:

        Stats {   dev: 2114,  // 設備的數字標識符   ino: 48064969,  // 設備的索引號   mode: 33188,  // 文件類型和模式   nlink: 1,  // 文件的硬鏈接數   uid: 85,  // 該文件擁有者的標識符   gid: 100,  // 擁有該文件的群組的標識符   rdev: 0,  // 數字型設備表標識符   size: 527,  // 文件大小,單位為字節   blksize: 4096,  // 文件系統塊的大小   blocks: 8,  // 文件系統為當前文件分配的塊數   atimeMs: 1318289051000.1,  // 上次被訪問時間   mtimeMs: 1318289051000.1,  // 上次被修改的時間   ctimeMs: 1318289051000.1,  // 上次更改文件狀態的時間   birthtimeMs: 1318289051000.1,  // 文件的創建時間   atime: Mon, 10 Oct 2011 23:24:11 GMT,  // 以上四個時間的另一種格式   mtime: Mon, 10 Oct 2011 23:24:11 GMT,   ctime: Mon, 10 Oct 2011 23:24:11 GMT,   birthtime: Mon, 10 Oct 2011 23:24:11 GMT    }

        每個Stats對象還可以返回一個bigint類型的結果,它的每個結果都是bigint類型,而不是上面的number類型,這里不再詳述。

        同時,它還有與Dirent相同的7個實例方法,來判斷當前的設備類型,請參考上述Dirent

        5. fs.WriteStream

        寫入流類,用于流式地寫入數據。它由fs.createWriteStream()創建和返回。

        fs.ReadStream類似,它也有closeopenready三個事件,兩者的用法也是一樣的。

        每個WriteStream實例也都有三個實例屬性:writeStream.bytesWrittenwriteStream.pathwriteStream.pending,分別表示當前已寫入的字節數、寫入流文件路徑和是否已就緒,與ReadStream也是類似的。

        三、fs模塊的常用方法

        fs模塊提供了很多讀寫文件系統資源的函數,上一部分介紹的類主要是對這些函數操作結果的封裝。下面來介紹一些常用的方法:

        1. fs.access(path [,mode], callback)

        檢查文件的可用性。

        第一個參數path為文件路徑,可以是字符串、Buffer或URL類型;
        第二個參數為要檢查的類型,可能的值包括fs.constants.F_OK(是否存在,默認值)、fs.constants.R_OK(是否可讀)和fs.constants.W_OK(是否可寫);
        第三個參數為回調函數,如果檢查失敗,則會傳入一個Error對象,否則會傳入undefined。

        比如我們需要檢查當前文件夾下是否存在package.json,可以這樣寫:

        fs.access('package.json', fs.constants.F_OK, (err) => {   if (err) {     ... // 文件存在   } else {     ... // 文件不存在   }})

        一般來說,不應該在檢查文件可用性之后立即讀寫文件,因為從檢查文件可用性到實際讀寫文件的過程中,該文件的狀態可能發生變化(比如其他進程操作了該文件),這會導致文件可用性檢查失效。因此fs.access方法通常只用來檢查文件的可用性,而要讀寫文件的話,可以直接調用讀寫文件的方法,再根據回調函數接收到的可能的異常來判斷該文件是否可用。

        該方法還有一個同步版本:fs.accessSync(path [,mode]),前兩個參數與異步版本是一致的。當文件檢查成功時,該方法返回undefined,否則將拋出異常,用法如下:

        try {   fs.accessSync('package.json', fs.constants.R_OK | fs.constants.W_OK);   console.log('可以讀寫');   } catch (err) {   console.error('無權訪問');   }

        2. fs.appendFile(path, data[, options], callback)

        向文件中追加數據。

        path支持字符串、Buffer和URL類型;
        data為要追加的數據,可以是字符串或Buffer實例;
        options為配置對象,包含三個參數:

        • encoding,編碼方式,默認為utf8

        • mode,模式,默認為0o666。該參數為文件的權限描述,0o666表示為每個用戶擁有讀寫權限。

        • flag,文件操作方式,默認為a,即追加。

        callback為函數執行完畢的回調函數,如果追加失敗,則第一個參數為錯誤對象,否則為undefined。例子如下:

        fs.appendFile('README.md', '這是要添加的數據', (err) => {   if (err) {     console.log('數據追加失敗');   } else {     console.log('數據追加成功');   }})

        該方法的同步版本為fs.appendFileSync(path, data[, options])。與accessSync一樣,它也只是移除了最后一個參數callback,通過返回值來判斷操作結果,這里不再重復舉例。

        3. fs.chmod(path, mode, callback)

        修改文件的權限。

        文件系統中每個文件都有讀、寫和執行三個權限,用r、w和x表示。其中r的權重為4,w的權重為2,x的權重為1,即當用戶擁有讀權限時,權限值加4,擁有寫權限時權限值加2,擁有執行權限時權限值加1。

        權限類型 權重
        讀權限 4
        寫權限 2
        執行權限 1

        這種權重分配使得任何一種權限擁有情況的權限值都是不同的,因此只需要一個八進制數字就可以表示某個用戶對某個文件擁有哪些權限。

        比如某個用戶擁有對某個文件的讀寫權限,而沒有執行權限,則他的權限值為4 + 2 + 0 = 6。而如果同時擁有這三個權限,則權限值為4 + 2 + 1 = 7

        對一個文件而言,系統中存在三類用戶:文件的擁有者、擁有該文件的用戶組和其他用戶。文件系統使用三個八進制數字,來表示這三類用戶對某個文件的權限。比如當某個文件的權限值為0o761時,它表示:

        • 文件的擁有者具備讀、寫和執行權限:7 = 4 + 2 + 1

        • 文件所屬的用戶組成員具備讀寫權限:6 = 4 + 2 + 0

        • 其他成員只具備執行權限:1 = 0 + 0 + 1

        fs.chmod方法就是用來修改某個文件權限的。它可以傳入三個參數,分別是文件路徑、權限值和回調函數。權限值就是上面所講的三個八進制數字,如0o761;如果修改失敗,則回調函數接收異常對象,否則沒有參數。

        該方法的同步版本為fs.chmodeSync(path, mode),用法與其他同步方法一致。

        4. fs.chown(path, uid, gid, callback)

        修改文件的擁有者。

        第一個參數為文件路徑;第二個參數為新的文件擁有者的用戶標識符;第三個參數為該文件所屬的用戶組的標識符,最后一個參數為執行完畢后的回調函數。

        uid和gid都是number類型,是文件系統為每個用戶和用戶組分配的唯一id,callback則會在操作失敗時得到一個錯誤對象。

        該方法的同步版本為fs.chownSync(path, uid, gid),使用方法與其他同步方法一致。

        5. fs.close(fd, callback)

        異步地關閉一個文件。

        第一個參數fd是調用fs.open打開一個文件時,文件系統為該文件分配的一個數字類型的文件描述符;當文件關閉失敗時,callback會得到一個異常對象。如:

        fs.open('README.md', 'a', (err, fd) => {   ...   fs.close(fd, (err) => {     if (err) {}  // 關閉失敗   });   })

        該方法的異步版本為fs.closeSync(fd)

        6. fs.constants

        fs模塊提供的常量,參見fs常量。

        7. fs.copyFile(src, dest[, mode], callback)

        拷貝一個文件。

        第一個參數src為拷貝源的地址,數據類型與path一致;
        第二個參數dest為目標地址,數據類型也與path一致;
        第三個可選參數為拷貝參數,默認情況下,將創建或覆蓋(文件已存在時)目標文件。除了這個默認值,還有以下三個可選值:

        • fs.constants.COPYFILE_EXCL – 如果 dest 已存在,則拷貝操作將失敗。

        • fs.constants.COPYFILE_FICLONE – 拷貝操作將嘗試創建寫時拷貝(copy-on-write)鏈接。如果平臺不支持寫時拷貝,則使用后備的拷貝機制。

        • fs.constants.COPYFILE_FICLONE_FORCE – 拷貝操作將嘗試創建寫時拷貝鏈接。如果平臺不支持寫時拷貝,則拷貝操作將失敗。

        第四個參數為為執行之后回調函數,與其他異步函數一致,它也會在失敗時接收到一個異常對象。

        它的同步版本為fs.copyFileSync(src, dest[, mode])

        8. fs.createReadStream(path[, options])

        創建一個讀取流。常見用法為:

        let fs = require('fs'); let rs = fs.createReadStream('./1.txt',{     highWaterMark:3, //文件一次讀多少字節,默認 64*1024     flags:'r', //默認 'r'     autoClose:true, //默認讀取完畢后自動關閉     start:0, //讀取文件開始位置     end:3, //流是閉合區間 包含start也含end     encoding:'utf8' //默認null }); rs.on("open",()=>{    console.log("文件打開") }); // 自動觸發data事件 直到讀取完畢 rs.on('data',(data)=>{     console.log(data); });

        9. fs.createWriteStream(path[, options])

        創建一個寫入流。常見用法為:

        const fs=require('fs'); const path=require('path'); let writeStream=fs.createWriteStream('./test/b.js',{encoding:'utf8'});  //讀取文件發生錯誤事件 writeStream.on('error', (err) => {     console.log('發生異常:', err); }); //已打開要寫入的文件事件 writeStream.on('open', (fd) => {     console.log('文件已打開:', fd); }); //文件寫入完成事件 writeStream.on('finish', () => {     console.log('寫入已完成..');     console.log('讀取文件內容:', fs.readFileSync('./test/b.js', 'utf8')); //打印寫入的內容     console.log(writeStream); });   //文件關閉事件 writeStream.on('close', () => {     console.log('文件已關閉!'); });   writeStream.write('這是我要做的測試內容'); writeStream.end();

        10. 基于文件描述符的一組函數

        fs.fchown(fd, callback)fs.fchmod(fd, callbakc)等,它是將第一個參數從path替換為了文件對應的文件描述符,使用這類方法之前需要先通過fs.open`打開文件,獲取文件描述符。這里不再詳述。

        11. fs.mkdir(path[, options], callback)

        創建一個目錄(即文件夾)。

        第一個參數path為要創建文件夾的路徑;
        第二個參數options支持兩個參數:

        • recursive,是否遞歸創建父目錄,默認為false。

        • mode,創建的文件夾的權限,默認是0o777。

        第三個參數為執行完畢的回調函數,它的第一個參數為可能的異常對象,當recursivetrue時,它還會得到一個path參數,值為當前操作創建的第一個目錄。

        而當要創建的目錄已經存在時,如果recursive為false,則會拋出異常,否則不會執行任何操作。

        創建目錄的用法如下:

        fs.mkdir('nodejs/lib', {recursive: true}, (err, path) => {   ...})

        該代碼試圖在當前路徑下的nodejs文件夾內創建lib文件夾,并且要遞歸地創建父目錄。即假如當前目錄下沒有nodejs文件夾,則先創建它。創建完成后,如果nodejs文件夾是新創建的,則path就是它的路徑;如果nodejs已經存在了,則path是新創建的lib文件夾的路徑。

        該方法的同步版本為fs.mkdirSync(path[, options])

        12. fs.open(path[, flags[, mode]], callback)

        打開一個文件。

        第一個參數為要打開的文件的路徑;
        第二個參數為打開方式,如r(只讀),w(只寫),a(追加)等;
        第三個參數為文件權限,默認為0o666(讀寫權限);
        最后一個參數為回調函數,它有兩個參數,第一個為可能拋出的異常對象,第二個是文件系統為被打開的文件分配的數值類型的文件描述符fd
        如:

        fs.open('README.md', 'r', (err, fd) => {   ...   fs.close(fd, (err) => {})})

        它的同步版本為fs.openSync(path[, flags[, mode]])

        13. fs.opendir(path[, options], callback)

        打開一個目錄。

        第一個參數為目錄的路徑;
        第二個參數包含兩個參數:

        • encoding,編碼類型,默認utf8

        • bufferSize,操作該目錄時要緩沖的目錄項的數量,默認值32,值越大則性能越好,但內存消耗會更大

        第三個參數為callback,即打開目錄后的回調函數。該回調函數可接受兩個值,一個是可能的異常對象,另一個是打開的目錄對象,類型為fs.Dir

        它的同步版本為fs.opendirSync(path[, options])

        14. fs.readFile(path[, options], callback)

        異步地讀取文件數據。

        該方法會默認先用fs.open打開文件,并在讀取完畢后自動關閉文件。因此調用該方法不需要手動打開和關閉文件,不過如果需要頻繁地操作文件,則該方法會導致文件被反復打開和關閉,造成性能下降。

        path除了可以是文件路徑外,還可以是文件描述符。如果傳入的是文件描述符,則默認在讀取完畢后不會關閉文件,并且后續的讀取會接著上次讀取的位置繼續向后。

        options支持以下兩個參數:

        • encoding,編碼格式,默認為null,實際使用時一般傳入'utf8'

        • flag,讀取方式,默認為只讀r

        如果只需要指定編碼格式,options還可以是一個字符串,如'utf8'

        回調函數的第一個參數為可能的異常對象,第二個參數則是從文件中讀出的數據,可能的數據類型為字符串或Buffer。如

        fs.readFile('README.md', 'utf8', (err, data) => {   if (err) { console.log('讀取失敗'); }   else {     console.log(data);   }})

        該方法的同步版本為fs.readFileSync(path[, options])

        另外該方法還有一個可替代方法,fs.read,請參考fs.read。它是在使用fs.open打開文件時通用的讀取文件的方法,對讀取過程的控制粒度更細。在對同一個文件進行頻繁的讀取操作,一般使用該方法。

        15. fs.rename(oldPath, newPath, callback)

        將oldPath指定的文件重命名為newPath指定的文件,如果該文件已存在,則覆蓋它。

        回調函數只接收可能拋出的異常對象。

        它的同步版本為fs.renameSync(oldPath, newPath)

        16. fs.stat(path[, options], callback)

        獲取某個文件的詳細信息。

        fs.stat('README.md', {bigint: true}, function(err, stats) {   console.log(stats.isDirectory());   console.log(stats);   });

        options僅支持bigint這一個參數,表示返回的stats對象是bigint類型,而不是通常的number類型。以上操作的結果請參考fs.Stats類。

        它的同步版本為fs.statSync(path[, options], callback)

        17. fs.writeFile(file, data[, options], callback)

        類似于fs.readFile,該方法為文件的寫方法。

        file參數為要寫入的文件路徑,或文件描述符。
        data為要寫入的數據,支持<string> 、 <Buffer> 、 <TypedArray> 、<DataView>等。
        options支持三個參數:encoding,默認值’utf8’;mode,權限類型,默認值0o666;flag,打開方式,默認值w。

        回調函數僅支持一個參數,即可能拋出的異常對象。

        該方法的同步版本為fs.writeFileSync(file, data[, options])

        當使用fs.open打開一個文件時,一般使用fs.write進行數據寫入,請參考fs.write。該方法可以精確地控制寫入位置,并且可以連續寫入數據。

        總結

        以上所列舉的只是fs模塊中較為常用的一些api,官方文檔中還有很多其他用途的接口,感興趣的請參考nodejs中文網 – fs模塊。

        如果需要在async函數中使用上述api,可以調用fs模塊提供的promise封裝版本,如:

        let fsPromises = require('fs').promises; // 或let fsPromises = require('fs/promises'); // 或let fs = require('fs'); let fsPromises = fs.promises;  fsPromise.readFile().then((err, data) => {   ... })  async function print (path) {   try {     let data = await fsPromise.readFile(path, 'utf8');     console.log(data); // 讀取成功,輸出data   } catch (e => {     ...  // 讀取失敗   }) }

        【推薦學習:《nodejs 教程》】

        贊(1)
        分享到: 更多 (0)
        網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
        主站蜘蛛池模板: 久久99国产精品成人欧美| 精品无码一级毛片免费视频观看| 国产成人久久久精品二区三区| 亚洲日韩中文在线精品第一 | 日韩精品无码久久久久久| 国产精品免费AV片在线观看| 久久久无码精品亚洲日韩软件| 国产成人精品亚洲日本在线| 亚洲国产精品va在线播放| 日产国产精品亚洲系列| 国模精品一区二区三区| 3级黄性日本午夜精品| 国产精品久久久久AV福利动漫| 中文字幕无码精品三级在线电影| 精品久久久久中文字幕一区| 在线精品视频播放| 国产亚洲精品一品区99热| 国产精品乱码高清在线观看| 日韩人妻无码精品一专区| 亚洲欧美日韩国产精品影院 | 麻豆国内精品久久久久久| 99精品欧美一区二区三区| 国产精品影音先锋| 97在线精品视频| 992tv精品视频tv在线观看| 久久精品国产亚洲AV麻豆网站| 中文字幕九七精品乱码| 亚洲午夜成人精品电影在线观看| 欧美精品色精品一区二区三区| 久久99精品免费一区二区| 国产午夜精品一区二区三区小说| 99久久人人爽亚洲精品美女| 欧美一卡2卡3卡四卡海外精品| 精品四虎免费观看国产高清午夜 | 四虎精品亚洲一区二区三区| 久久久久国产成人精品亚洲午夜| 久久精品无码一区二区三区日韩 | 97久久精品无码一区二区天美| 精品国精品无码自拍自在线| 久久久久女人精品毛片| 久久精品国产清高在天天线|