最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Node.js編寫一個(gè)文件同步CLI工具

 更新時(shí)間:2026年05月19日 09:19:50   作者:用戶155831996814  
本文主要和大家分享一個(gè)基于Node.js編寫的文件同步CLI工具,該工具可以監(jiān)聽本地目錄的變化,并將變化同步到遠(yuǎn)程服務(wù)器,作者詳細(xì)介紹了工具的技術(shù)選型、核心代碼、實(shí)現(xiàn)細(xì)節(jié)和測試過程,并分享了一些優(yōu)化方向

作為天天在終端里泡著的前端er,我手頭有個(gè)痛點(diǎn)忍了很久了:每次改完靜態(tài)文件,得手動(dòng)scp到服務(wù)器,或者開FileZilla拖拽。效率低不說,還容易漏文件。尤其是趕項(xiàng)目上線那會(huì)兒,頻繁改個(gè)小圖標(biāo)都要等半分鐘傳輸,體驗(yàn)極差。

于是我花了兩晚上,寫了個(gè)文件同步CLI工具,叫filesync。思路很簡單——監(jiān)聽本地目錄變化,增量同步到遠(yuǎn)程服務(wù)器。整個(gè)工具大概400行代碼,今天把核心邏輯拆解一下,給有類似需求的同學(xué)參考。

先說技術(shù)選型

Node.js寫CLI工具是真的舒服,生態(tài)豐富,上手快。我調(diào)研了幾個(gè)關(guān)鍵包:

  • chokidar:監(jiān)聽文件變化,比原生fs.watch穩(wěn)得多,跨平臺(tái)兼容好
  • ssh2:SSH2客戶端庫,Node里做SFTP最靠譜的選擇
  • fs-extra:文件操作封裝,比原生fs順手
  • cliktcommander:命令行參數(shù)解析,我選了commander

核心就這四個(gè),沒有引入太復(fù)雜的依賴。

目錄結(jié)構(gòu)

filesync/
├── bin/
│   └── filesync.js      # CLI入口
├── src/
│   ├── index.js         # 主邏輯
│   ├── sync.js          # 同步核心
│   └── watcher.js       # 監(jiān)聽邏輯
├── package.json
└── README.md

bin/filesync.js非常簡潔:

#!/usr/bin/env node
const { program } = require('commander');
const { watch } = require('./src/watcher');
const { sync } = require('./src/sync');
program
  .option('-l, --local <path>', '本地目錄', process.cwd())
  .option('-r, --remote <path>', '遠(yuǎn)程目錄')
  .option('-h, --host <host>', '服務(wù)器地址')
  .option('-p, --port <port>', '端口', '22')
  .option('-u, --user <user>', '用戶名')
  .option('-k, --key <path>', '私鑰路徑')
  .parse(process.argv);
const opts = program.opts();
// 單次同步
if (opts.remote && opts.host) {
  sync(opts).then(() => {
    console.log('同步完成');
    process.exit(0);
  });
} else {
  // 監(jiān)聽模式
  watch(opts);
}

用法上,支持兩種模式。一種是單次同步:

filesync -l ./dist -r /var/www/app -h 101.132.45.23 -u root -k ~/.ssh/id_rsa

另一種是監(jiān)聽模式,文件變動(dòng)自動(dòng)觸發(fā):

filesync -l ./dist -r /var/www/app -h 101.132.45.23 -u root -k ~/.ssh/id_rsa

文件監(jiān)聽:watcher.js

這是第一塊核心代碼。我之前用過fs.watchFile,CPU占用高得嚇人,換成chokidar之后穩(wěn)多了。

const chokidar = require('chokidar');
const { sync } = require('./sync');

let pending = new Map();
let timer = null;

function watch(opts) {
  const { local } = opts;
  console.log(`監(jiān)聽 ${local} 目錄變化...`);
  
  const watcher = chokidar.watch(local, {
    ignored: /(^|[\/\\])\../,  // 忽略隱藏文件
    persistent: true,
    ignoreInitial: true,         // 忽略初始掃描
    awaitWriteFinish: {         // 等待寫入完成
      stabilityThreshold: 300,
      pollInterval: 100
    }
  });

  watcher
    .on('add', path => handleChange('add', path, opts))
    .on('change', path => handleChange('change', path, opts))
    .on('unlink', path => handleChange('unlink', path, opts));
}

function handleChange(event, path, opts) {
  const key = `${event}:${path}`;
  pending.set(key, Date.now());
  
  // 300ms內(nèi)的多次變動(dòng),合并為一次同步
  if (timer) clearTimeout(timer);
  timer = setTimeout(async () => {
    const ops = Array.from(pending.entries());
    pending.clear();
    
    for (const [key, time] of ops) {
      const [event, filePath] = key.split(':');
      console.log(`[${event}] ${filePath}`);
      await sync({ ...opts, files: [filePath] });
    }
  }, 300);
}

module.exports = { watch };

有個(gè)細(xì)節(jié)我踩過坑:Node.js的fs.watchchokidar都存在"寫入中"檢測問題。圖片或者大文件還在寫入,chokidar就觸發(fā)change事件了。解決方案是加awaitWriteFinish,等文件穩(wěn)定300ms再處理。這個(gè)數(shù)值是我本地測試出來的,文件越大可能需要調(diào)整到500ms甚至更高。

SFTP同步:sync.js

增量的精髓在于只傳變化的文件,不做全量覆蓋。SFTP連接這塊我封裝了一個(gè)工廠函數(shù):

const Client = require('ssh2').Client;
const fs = require('fs-extra');
const path = require('path');
const { SftpSync } = require('./sftp-wrapper');

async function sync(opts) {
  const { local, remote, host, port, user, key, files } = opts;
  
  const client = new Client();
  
  await new Promise((resolve, reject) => {
    client.connect({
      host,
      port: parseInt(port),
      username: user,
      privateKey: fs.readFileSync(key)
    });
    
    client.on('ready', resolve).on('error', reject);
  });
  
  const sftp = new SftpSync(client);
  
  for (const file of files) {
    const relativePath = path.relative(local, file);
    const remotePath = path.posix.join(remote, relativePath);
    
    if (fs.statSync(file).isDirectory()) {
      await sftp.mkdir(remotePath, { recursive: true });
    } else {
      await sftp.put(file, remotePath);
      console.log(`  -> ${remotePath}`);
    }
  }
  
  client.end();
}

module.exports = { sync };

SFTP的包裝類我單獨(dú)拆了出來,因?yàn)樯婕暗竭B接管理和隊(duì)列控制:

class SftpSync {
  constructor(client) {
    this.sftp = null;
    this.client = client;
    this.queue = Promise.resolve();
  }
  
  getSftp() {
    if (!this.sftp) {
      this.sftp = new Promise((resolve, reject) => {
        this.client.sftp((err, sftp) => {
          if (err) reject(err);
          else resolve(sftp);
        });
      });
    }
    return this.sftp;
  }
  
  async put(localPath, remotePath) {
    await this.getSftp();
    const sftp = await this.sftp;
    
    return new Promise((resolve, reject) => {
      sftp.fastPut(localPath, remotePath, {}, (err) => {
        if (err) reject(err);
        else resolve();
      });
    });
  }
  
  async mkdir(remotePath, opts = {}) {
    await this.getSftp();
    const sftp = await this.sftp;
    
    return new Promise((resolve, reject) => {
      sftp.mkdir(remotePath, opts, (err) => {
        // 目錄已存在不報(bào)錯(cuò)
        if (err && err.code !== 4) reject(err);
        else resolve();
      });
    });
  }
}

這里用Promise鏈做隊(duì)列控制,避免并發(fā)寫入同一個(gè)文件導(dǎo)致?lián)p壞。每次put操作都會(huì)加到隊(duì)列尾部,串行執(zhí)行。

遠(yuǎn)程路徑映射的坑

做了才知道,Windows和Linux的路徑格式不一樣。Windows是\,Linux是/。寫的時(shí)候直接用path.join會(huì)出問題,比如本地路徑dist\assets\logo.png傳到服務(wù)器變成dist\assets\logo.png,Linux根本識(shí)別不了。

解決方案是用path.posix.join處理所有遠(yuǎn)程路徑:

const remotePath = path.posix.join(remote, path.relative(local, file));

這樣無論本地是Windows還是Mac,遠(yuǎn)程路徑永遠(yuǎn)是Unix風(fēng)格。

測試過程

我拿一個(gè)Vue3項(xiàng)目測試,dist目錄大概50MB,200多個(gè)文件。第一次全量同步花了12秒,后續(xù)單文件修改平均80ms觸達(dá)。測試場景如下:

  • 修改單個(gè)JS文件:平均耗時(shí)150ms(含監(jiān)聽延遲300ms + 傳輸 + SSH握手)
  • 修改單個(gè)圖片:平均耗時(shí)300ms
  • 連續(xù)修改多個(gè)文件:合并為一次同步,大概500ms

CPU占用方面,監(jiān)聽模式下chokidar穩(wěn)定在0.5%以下,SSH連接占用1%左右。內(nèi)存 footprint 非常小,我的Mac Air M1跑起來毫無壓力。

還能怎么改進(jìn)

現(xiàn)在這版還有很多可以優(yōu)化的地方:

  1. 增量對比:目前只對比文件名和時(shí)間戳,沒有MD5校驗(yàn),大文件可能會(huì)有誤差
  2. 斷點(diǎn)續(xù)傳:大文件傳一半斷了就得重來
  3. 多端同步:一個(gè)源同步到多臺(tái)服務(wù)器
  4. 壓縮傳輸:開啟SSH壓縮,減少帶寬占用

這些功能我在規(guī)劃里了,有時(shí)間慢慢加上。

結(jié)語

整個(gè)工具核心邏輯就這些,代碼量不大,但解決了我的實(shí)際問題。寫這個(gè)的收獲是:日常開發(fā)中那些讓你皺眉的重復(fù)操作,都值得花時(shí)間自動(dòng)化。工具寫好之后,注意力才能真正放回業(yè)務(wù)邏輯上。

完整代碼我放到了GitHub,有興趣的可以參考:github.com/xxx/filesync。有問題歡迎提Issue,覺得有用的話給個(gè)star就更好了。

到此這篇關(guān)于使用Node.js編寫一個(gè)文件同步CLI工具的文章就介紹到這了,更多相關(guān)Node.js文件同步內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Nodejs中使用puppeteer控制瀏覽器中視頻播放功能

    Nodejs中使用puppeteer控制瀏覽器中視頻播放功能

    本項(xiàng)目主要功能為在瀏覽器中自動(dòng)播放視頻,并且實(shí)現(xiàn)音量控制,快進(jìn)快退,全屏控制,播放暫停控制等功能。對Nodejs中使用puppeteer控制瀏覽器中視頻播放功能感興趣的朋友跟隨小編一起看看吧
    2019-08-08
  • 詳解Node.js實(shí)現(xiàn)301、302重定向服務(wù)

    詳解Node.js實(shí)現(xiàn)301、302重定向服務(wù)

    這篇文章主要介紹了詳解Node.js實(shí)現(xiàn)301、302重定向服務(wù),詳細(xì)的介紹了用Nodejs的http模塊,實(shí)現(xiàn)一個(gè)301或302重定服務(wù)。
    2017-04-04
  • node?NPM庫promise?異步任務(wù)狀態(tài)管理

    node?NPM庫promise?異步任務(wù)狀態(tài)管理

    這篇文章主要介紹了node?NPM庫promise?異步任務(wù)狀態(tài)管理
    2023-07-07
  • Node.js使用Express.Router的方法

    Node.js使用Express.Router的方法

    這篇文章主要為大家詳細(xì)介紹了Node.js使用Express.Router的方法 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 使用?Node-RED對?MQTT?數(shù)據(jù)流處理

    使用?Node-RED對?MQTT?數(shù)據(jù)流處理

    本文將介紹使用 Node-RED 連接到 MQTT 服務(wù)器,并對 MQTT 數(shù)據(jù)進(jìn)行過濾和處理后再將其發(fā)送至 MQTT 服務(wù)器的完整操作流程。讀者可以快速了解如何使用 Node-RED 對 MQTT 數(shù)據(jù)進(jìn)行簡單的流處理
    2022-05-05
  • Node.js中的WebSocket底層實(shí)現(xiàn)

    Node.js中的WebSocket底層實(shí)現(xiàn)

    WebSockets是基于HTTP的雙向通信協(xié)議,允許客戶端和服務(wù)器之間實(shí)現(xiàn)實(shí)時(shí)、持久的數(shù)據(jù)交換,本文詳細(xì)介紹了使用JavaScript和Node.js創(chuàng)建WebSockets服務(wù)器和客戶端的過程,感興趣的可以了解一下
    2024-10-10
  • Node.js使用Sharp.js進(jìn)行圖像處理的實(shí)踐與技巧

    Node.js使用Sharp.js進(jìn)行圖像處理的實(shí)踐與技巧

    Sharp.js 是一個(gè)高性能的 Node.js 圖像處理庫,基于 C 語言編寫的 libvips 庫封裝而來,提供了便捷、高效的圖片編輯與轉(zhuǎn)換功能,以下是對 Sharp.js 的深入解析,包括全方位實(shí)踐與技巧,需要的朋友可以參考下
    2024-08-08
  • nodejs dgram模塊廣播+組播的實(shí)現(xiàn)示例

    nodejs dgram模塊廣播+組播的實(shí)現(xiàn)示例

    這篇文章主要介紹了nodejs dgram模塊廣播+組播的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • node中koa中間件機(jī)制詳解

    node中koa中間件機(jī)制詳解

    本篇文章主要主要介紹了node中koa中間件機(jī)制詳解,詳細(xì)的介紹了koa和兼容問題,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-08-08
  • nodejs+mysql實(shí)現(xiàn)用戶相關(guān)的增刪改查的詳細(xì)操作

    nodejs+mysql實(shí)現(xiàn)用戶相關(guān)的增刪改查的詳細(xì)操作

    這篇文章主要介紹了nodejs+mysql實(shí)現(xiàn)用戶相關(guān)的增刪改查的詳細(xì)操作的相關(guān)資料,需要的朋友可以參考下
    2023-05-05

最新評論

沿河| 奉节县| 岗巴县| 行唐县| 涟水县| 调兵山市| 宝兴县| 龙山县| 房山区| 博白县| 满洲里市| 蚌埠市| 丹凤县| 阜阳市| 沅江市| 东光县| 淮滨县| 辉县市| 凌云县| 石台县| 西充县| 汕尾市| 宁强县| 新干县| 闽清县| 孟津县| 秦皇岛市| 漳州市| 万荣县| 英吉沙县| 三江| 岳普湖县| 交口县| 甘孜县| 邹城市| 买车| 丹凤县| 滕州市| 阿勒泰市| 咸丰县| 仲巴县|