完整代码示例如下:
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数组保存了需要上传到服务器的图片文件路径(包括文件夹与文件名),感兴趣的朋友可以测试一下看看效果~