node ftp上传文件夹到服务器案例详解
作者:我说的马是黑马
这篇文章主要介绍了node ftp上传文件夹到服务器的视线方法,结合具体实例分析了node.js调用ftp模块进行文件上传的相关配置、连接、path路径操作与文件传输实现方法,需要的朋友可以参考下
完整代码示例如下:
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数组保存了需要上传到服务器的图片文件路径(包括文件夹与文件名),感兴趣的朋友可以测试一下看看效果~