node ftp上传文件夹到服务器案例详解

JavaScript/前端
304
0
0
2023-04-30
标签   NodeJs

完整代码示例如下:

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