完整代码示例如下:
const ftp = require('ftp');//连接FTP
const path = require('path');
const client = new ftp();
const fs = require('fs');
//本地文件夹路径;
const localDirPath = '/test/';
//远程地址,打开ftp以后的地址,不需要加入host;
const remotePath = '/';
const uploadFiles = [];
const mkDirPromiseArr = [];
const connectionProperties = {
host: '', //ftp地址;
user: '', //用户名;
password: '', //密码;
port: 21 //端口;
};
client.connect(connectionProperties);
client.on('ready', () => {
console.log('ftp client is ready');
start();
});
async function start() {
const { err: ea, dir } = await cwd(remotePath);//此处应对err做处理
if (ea) {
client.mkdir(remotePath, true, (err) => {
if (err) {
console.log('创建' + remotePath + '文件夹失败');
upload();
} else {
console.log('创建' + remotePath + '成功');
upload();
}
});
} else {
upload();
}
function upload() {
const filesPath = { files: [] };
getDirAllFilePath(localDirPath, filesPath);
remoteMkDir(filesPath, '');
console.log('准备上传...');
setTimeout(() => {
Promise.all(mkDirPromiseArr).then(() => {
console.log('开始上传...');
const tasks = uploadFile();
runPromiseArray(tasks).then(() => {
client.end();
console.warn('上传完成~');
});
});
}, 3000);
}
}
// 获取本地的文件地址和路径;
function getDirAllFilePath(paths, parent) {
const files = fs.readdirSync(paths);
files.forEach(item => {
if (item != '.DS_Store') {
const path = `${paths}/${item}`;
if (isDir(path)) {
getDirAllFilePath(path, parent[item] = { files: [] });
} else if (isFile(path)) {
parent.files.push(item);
}
}
})
}
//创建远程确实的文件夹;
async function remoteMkDir(obj, _path) {
for (const key in obj) {
if (key === 'files') {
for (let i = 0, len = obj[key].length; i < len; i++) {
const promise = new Promise(async resolve => {
let p = '';
if (_path) {
p = _path + '/';
}
const filePathName = p + obj[key][i];
uploadFiles.push({ path: filePathName, fileName: obj[key][i] });
const ph = remotePath + filePathName.substring(0, filePathName.lastIndexOf('/') + 1);
let { err: ea, dir } = await cwd(ph);//此处应对err做处理
if (ea) {
client.mkdir(ph, true, (err) => {
if (err) {
console.log('mkdir' + ph + 'err', err);
resolve(null);
return;
}
console.log('mkdir ' + ph + ' success');
resolve(null);
});
} else {
resolve(null);
}
});
mkDirPromiseArr.push(promise);
}
} else {
let p = '';
if (_path) {
p = _path + '/';
}
remoteMkDir(obj[key], p + key);
}
}
}
//上传文件;
function uploadFile() {
const tasks = [];
const resourcesPath = localDirPath;
//目标路径文件夹;
const checkPath = remotePath;
for (let i = 0, len = uploadFiles.length; i < len; i++) {
const task = () => {
return new Promise(async (resolve, reject) => {
const _path = uploadFiles[i].path;
const targetPath = checkPath + _path;
const putPath = resourcesPath + '/' + _path;
const dirpath = path.dirname(targetPath);
const fileName = path.basename(targetPath);
client.cwd(dirpath, (cwdErr, dir) => {
client.pwd((pwdErr, cwd) => {
if (pwdErr) {
resolve(pwdErr)
} else {
client.get(fileName, (err, res) => {
if (res) {
console.log(`${targetPath} =====================已经存在了`);
resolve(true);
} else {
const rs = fs.createReadStream(putPath);
client.put(rs, fileName, (putErr, data) => {
if (putErr) {
resolve(err);
} else {
console.log(targetPath + '文件上传成功');
resolve(true);
}
})
}
});
}
});
})
});
}
tasks.push(task);
}
return tasks;
}
//执行Promise的队列动作;
function runPromiseArray(parray) { //这个方法可以放到G里
let p = Promise.resolve();
for (let promise of parray) {
p = p.then(promise);
}
return p;
}
//切换目录
async function cwd(dirpath) {
return new Promise((resolve, reject) => {
client.cwd(dirpath, (err, dir) => {
resolve({ err: err, dir: dir });
})
});
}
function isFile(filepath) { //判断是否是文件 Boolean
let stat = fs.statSync(filepath)
return stat.isFile()
}
function isDir(filepath) { //判断是否是文件夹 Boolean
let stat = fs.statSync(filepath);
return stat.isDirectory();
}
笔者解读一下:代码中的localDirPath为本地需要读取其中文件,并使用ftp上传的文件夹。
注意:这里的上传是针对文件夹中所有的文件夹与文件进行遍历后的上传,实际应用中,我们可能只需要上传指定的文件,对此,笔者修改后的脚本如下:
const ftp = require('ftp');//连接FTP
const path = require('path');
const client = new ftp();
const fs = require('fs');
//本地文件夹路径;
const localDirPath = path.join(__dirname,'/imgtmp/');//待遍历的本地目录
//远程ftp服务器文件路径
let remotePath = '/yourweb/images/';
const upFileList = ["202304/202304061415075072.png","202304/202304061415075073.png"];//手动设置需要上传的文件
const uploadFiles = [];
const mkDirPromiseArr = [];
client.on('ready',()=>{
console.log('ftp client is ready');
});
const connconfig = {
host: '', //ftp地址;
user: '', //用户名;
password: '', //密码;
port: 21 //端口;
}
client.connect(connconfig);
client.on('ready', () => {
console.log('ftp client is ready');
start();
});
async function start() {
const { err: ea, dir } = await cwd(remotePath);//此处应对err做处理
if (ea) {
client.mkdir(remotePath, true, (err) => {
if (err) {
console.log('创建' + remotePath + '文件夹失败');
upload();
} else {
console.log('创建' + remotePath + '成功');
upload();
}
});
} else {
upload();
}
function upload() {
console.log("mkDirPromiseArr==>",mkDirPromiseArr);
// const filesPath = { files: [] };
// getDirAllFilePath(localDirPath, filesPath);
const filesPath = { files: upFileList };//直接给定需要上传的文件列表
// console.log("遍历之后的filesPath===>",filesPath);
remoteMkDir(filesPath, '');
console.log('准备上传...');
setTimeout(() => {
Promise.all(mkDirPromiseArr).then(() => {
console.log('开始上传...');
const tasks = uploadFile();
runPromiseArray(tasks).then(() => {
client.end();
console.warn('上传完成~');
});
});
}, 1000);
}
}
// 获取本地的文件地址和路径;
function getDirAllFilePath(paths, parent) {
const files = fs.readdirSync(paths);
files.forEach(item => {
if (item != '.DS_Store') {
const path = `${paths}/${item}`;
if (isDir(path)) {
getDirAllFilePath(path, parent[item] = { files: [] });
} else if (isFile(path)) {
parent.files.push(item);
}
}
})
}
//创建远程缺失的文件夹;
async function remoteMkDir(obj, _path) {
for (const key in obj) {
if (key === 'files') {
for (let i = 0, len = obj[key].length; i < len; i++) {
const promise = new Promise(async resolve => {
let p = '';
if (_path) {
p = _path + '/';
}
const filePathName = p + obj[key][i];
// const filePathName = path.dirname(obj[key][i]);
const fileName = path.basename(obj[key][i]);
// uploadFiles.push({ path: filePathName, fileName: obj[key][i] });
uploadFiles.push({ path: filePathName, fileName: fileName });
const ph = remotePath + filePathName.substring(0, filePathName.lastIndexOf('/') + 1);
let { err: ea, dir } = await cwd(ph);//此处应对err做处理
if (ea) {
client.mkdir(ph, true, (err) => {
if (err) {
console.log('mkdir' + ph + 'err', err);
resolve(null);
return;
}
console.log('mkdir ' + ph + ' success');
resolve(null);
});
} else {
resolve(null);
}
});
mkDirPromiseArr.push(promise);
}
} else {
let p = '';
if (_path) {
p = _path + '/';
}
remoteMkDir(obj[key], p + key);
}
}
}
//上传文件;
function uploadFile() {
const tasks = [];
const resourcesPath = localDirPath;
//目标路径文件夹;
const checkPath = remotePath;
for (let i = 0, len = uploadFiles.length; i < len; i++) {
const task = () => {
return new Promise(async (resolve, reject) => {
const _path = uploadFiles[i].path;
// const _path = uploadFiles[i];
const targetPath = checkPath + _path;
const putPath = resourcesPath + '/' + _path;
const dirpath = path.dirname(targetPath);
const fileName = path.basename(targetPath);
client.cwd(dirpath, (cwdErr, dir) => {
client.pwd((pwdErr, cwd) => {
if (pwdErr) {
resolve(pwdErr)
} else {
client.get(fileName, (err, res) => {
if (res) {
console.log(`${targetPath} =====================已经存在了`);
resolve(true);
} else {
const rs = fs.createReadStream(putPath);
client.put(rs, fileName, (putErr, data) => {
if (putErr) {
resolve(err);
} else {
console.log(targetPath + '文件上传成功');
resolve(true);
}
})
}
});
}
});
})
});
}
tasks.push(task);
}
return tasks;
}
//执行Promise的队列动作;
function runPromiseArray(parray) { //这个方法可以放到G里
let p = Promise.resolve();
for (let promise of parray) {
p = p.then(promise);
}
return p;
}
//切换目录
async function cwd(dirpath) {
return new Promise((resolve, reject) => {
client.cwd(dirpath, (err, dir) => {
resolve({ err: err, dir: dir });
})
});
}
function isFile(filepath) { //判断是否是文件 Boolean
let stat = fs.statSync(filepath)
return stat.isFile()
}
function isDir(filepath) { //判断是否是文件夹 Boolean
let stat = fs.statSync(filepath);
return stat.isDirectory();
}
PS:笔者此处使用了一个upFileList数组保存了需要上传到服务器的图片文件路径(包括文件夹与文件名),感兴趣的朋友可以测试一下看看效果~