前端开发只是前端项目的一部分,开发完还需要部署,在没有持续集成的情况下,每次手动构建(build)、上传,还要借助xftp等第三方的软件。重复繁琐的手动工作。最近在npm仓库发现了一个
ftp
的包,于是想到基于该包封装个简单的函数:根据配置的用户名
和密
码,以及源文件目录
、服务器文件目录
,执行上传。
效果图如下:
环境配置
mkdir ftp-loader
、cd ftp-loader
、npm init
新建并初始化项目
npm i ftp ora
安装依赖 :ftp 核心依赖、ora是一款spinner库(loading效果
)
编写入口文件
编写一个函数,接收一个对象有五个属性:域名
、用户名
、密码
、上传文件的目录
、服务器目录
。
实现步骤如下:
- 引入
ftp库
、ora库
- 通过ftp连接服务器,
ready
后开始上传,并开始loading - 从起点开始,读取文件或递归读取目录,并将文件路径(包括文件名)放入一个数组(只存放有效的文件,如果是文件夹读取下面的文件)。
- 遍历文件路径数组,借助
promise
对象如果根目录下直接发送,如果有子文件夹,先在服务器创建文件夹再发送,如果再有深层次的文件夹则继续下潜。 - 待所有文件发送完毕后,停止
loading
、关闭连接。
遇到的问题:
当文件目录下有子目录时需要在服务端创建相应的目录,该过程是异步的。 发送文件的过程也是异步的。
读取文件并收集文件路径到数组过程需要是同步的。
client
的mkdir
方法有个递归选项可以直接创建父目录,为了避免异步、同步的可能产生的问题,我们采用该方法创建目录。
由于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.
为了方便有兴趣的读者测试,我在测试文件直接放了域名、用户名、密码。