发布一款ftp部署前端资源的npm包

954 阅读3分钟

前端开发只是前端项目的一部分,开发完还需要部署,在没有持续集成的情况下,每次手动构建(build)、上传,还要借助xftp等第三方的软件。重复繁琐的手动工作。最近在npm仓库发现了一个ftp的包,于是想到基于该包封装个简单的函数:根据配置的用户名码,以及源文件目录服务器文件目录,执行上传。

效果图如下:

环境配置

mkdir ftp-loadercd ftp-loadernpm init 新建并初始化项目

npm i ftp ora安装依赖 :ftp 核心依赖、ora是一款spinner库(loading效果

编写入口文件

编写一个函数,接收一个对象有五个属性:域名用户名密码上传文件的目录服务器目录

实现步骤如下:

  1. 引入ftp库ora库
  2. 通过ftp连接服务器,ready后开始上传,并开始loading
  3. 从起点开始,读取文件或递归读取目录,并将文件路径(包括文件名)放入一个数组(只存放有效的文件,如果是文件夹读取下面的文件)。
  4. 遍历文件路径数组,借助promise对象如果根目录下直接发送,如果有子文件夹,先在服务器创建文件夹再发送,如果再有深层次的文件夹则继续下潜。
  5. 待所有文件发送完毕后,停止loading、关闭连接。

遇到的问题:

当文件目录下有子目录时需要在服务端创建相应的目录,该过程是异步的。 发送文件的过程也是异步的。

读取文件并收集文件路径到数组过程需要是同步的。

clientmkdir方法有个递归选项可以直接创建父目录,为了避免异步、同步的可能产生的问题,我们采用该方法创建目录。

由于ftp库创建文件夹是异步的、发送文件也是异步的,因此需要借助promise对象,在Promise.all的then和catch里分别提示成功、失败,并关闭loading、连接

代码示例
var Client = require('ftp');
var fs = require('fs');
var spinner = require('ora')()

var client = new Client();


module.exports = function({ host,user,password,sourceDir,targetDir }){
    var filepaths = [];
    function readFilesFromDir(parentPath,filePath){
        let currentPath = "";
        if(parentPath === ""){
            currentPath = filePath;
        }else{
            currentPath = parentPath+'/'+filePath;
        }
        let sourcePath = `${sourceDir}/${currentPath}`;

        if(fs.lstatSync(sourcePath).isDirectory()){
            let files = fs.readdirSync(sourcePath);
            for(let filePath of files){
                readFilesFromDir(currentPath,filePath); 
            } 
        }else{
            filepaths.push(currentPath)
        }
        
    }

    function readyFn(){
        spinner.start();
        fs.readdir(sourceDir, function(err,files){
            if(err) throw err;
            for(let filePath of files){
                readFilesFromDir("",filePath);
            }
            transferFiles();
        })
    }
    function transferFile(filePath){
        return new Promise((res,rej)=>{
            let parentPath = filePath.lastIndexOf('/') !== -1 && filePath.substring(0 , filePath.lastIndexOf('/') );
            let sourcePath = `${sourceDir}/${filePath}`;
            let targetPath = `${targetDir}/${filePath}`;
            let newDirInServer = `${targetDir}/${parentPath}`;
            if(!parentPath || parentPath === '.'){
                client.put(sourcePath, targetPath,function(err) {
                    if (err) throw err;
                    console.log(sourcePath+'-----'+targetPath)
                    res(true);
                });
            }else{
                client.mkdir(newDirInServer,true,function(err){
                    if(err) throw err;
                    client.put(sourcePath, targetPath,function(err) {
                        if (err) throw err;
                        console.log(sourcePath+'-----'+targetPath)
                        res(true);
                    });
                })
            }
        })
    }
    function transferFiles(){
        let promises = [];
        filepaths.forEach(filePath=>{
            promises.push(transferFile(filePath))
        })
        Promise.all(promises).then(res=>{
            console.log('上传成功!')
            spinner.stop();
            client.end();
        }).catch(rej=>{
            console.log('上传失败!');
            spinner.stop();
            client.end();
        })

    }
    client.on('ready', readyFn);
    // connect to localhost:21 as anonymous
    client.connect({
        host,
        user,
        password
    });
}

测试

服务器:采用的西部数码的虚拟主机(便宜)

mkdir test-files新建测试文件夹并复制一份构建好的文件进去(dist文件夹)。

配置test.js文件:

var main  = require('./index')
main({
    host:'你的主机域名',
    user:'ftp用户名',
    password:'ftp密码',
    sourceDir:"./test-files/dist",//上传文件目录
    targetDir:"wwwroot/test-ftp"//服务端目标目录
})

npm run test 上传完毕后通过软件可看到所欲的文件已经在这了:

发布

npm publish,具体发布过程可参考我的另一篇文章:发布一款npm包帮助理解npm


全文完,所有代码已经上传至github.

为了方便有兴趣的读者测试,我在测试文件直接放了域名、用户名、密码。