nodejs通过http调用实现操作svn

nodejs想操作svn进行list和下载操作,但是不依赖运行环境支持svn命令,可以通过下面的代码实现

//const { DAVRepository } = require('node-svn-ultimate');
//const { exec } = require('child_process');  
//const { parseString } = require('xml2js');
//const svnUltimate = require('node-svn-ultimate');
//const SimpleSvn = require('simple-svn');
//const parseString = require('xml2js').parseString;
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const https = require('https');

const svnUrl = 'https://192.168.65.17:4321/svn/工程项目/IDP资源库';
const remoteSvnUrl = 'https://58.20.55.73:4321/svn/工程项目/IDP资源库';
const svnUsername = 'hz3000';
const svnPassword = 'hz3000admin';

async function listSvnFiles1(url) {
    const command = (url) => {
        let urlSuffix = url ? `/${url}` : '';
        return `svn list --non-interactive --trust-server-cert --xml --username ${svnUsername} --password ${svnPassword} ${svnUrl}${urlSuffix}`;
    }; 
    return new Promise((resolve, reject) => {
        exec(command(url), (error, stdout, stderr) => {
            if (error || stderr) {
                console.error(`连接SVN内网错误: ${error || stderr}`);
                const remoteCommand = (url) => {
                    let urlSuffix = url ? `/${url}` : '';
                    return `svn list --non-interactive --trust-server-cert --xml --username ${svnUsername} --password ${svnPassword} ${remoteSvnUrl}${urlSuffix}`;
                }; 
                exec(remoteCommand(url), (error, stdout, stderr) => {
                    if (error) {
                        console.error(`连接SVN外网错误: ${error || stderr}`);
                        reject(error || stderr);
                        return;
                    }
                    parseString(stdout, (err, result) => {
                        if (err) {
                            console.error(`解析XML错误: ${err}`);
                            reject(err);
                            return;
                        }
                        console.log('SVN仓库内容:');
                        //const files = result.lists.list[0].entry.map(entry => entry.name[0]);
                        const files = extractNamesAndDates(result.lists.list[0]);
                        console.info(files);
                        resolve(files);
                    });
                });
                return;
            }
            parseString(stdout, (err, result) => {
                if (err) {
                    console.error(`解析XML错误: ${err}`);
                    reject(err);
                    return;
                }
                console.log('SVN仓库内容:');
                //const files = result.lists.list[0].entry.map(entry => entry.name[0]);
                const files = extractNamesAndDates(result.lists.list[0]);
                console.info(files);
                resolve(files);
            });
        });
    });
}

async function listSvnFilesWithSvnUltimate(url) {
    const options = {
        username: svnUsername,
        password: svnPassword,
        trustServerCert: true,
        noAuthCache: true
    };

    return new Promise((resolve, reject) => {
        const svnUrlToUse = url ? `${svnUrl}/${url}` : svnUrl;
        console.info("svnUrlToUse:"+svnUrlToUse);
        svnUltimate.commands.list(svnUrlToUse, options, (err, data) => {
            if (err) {
                console.error(`svnUltimate连接SVN内网错误: ${err}`);
                
                // 尝试连接远程SVN
                const remoteSvnUrlToUse = url ? `${remoteSvnUrl}/${url}` : remoteSvnUrl;
                svnUltimate.commands.list(remoteSvnUrlToUse, options, (err, data) => {
                    if (err) {
                        console.error(`连接SVN外网错误: ${err}`);
                        reject(err);
                        return;
                    }
                    resolve(data.list.entry);
                    //parseSvnData(data, resolve, reject);
                });
                return;
            }
            console.info(data.list.entry);
            resolve(data.list.entry);
            //parseSvnData(data, resolve, reject);
        });
    });
}


async function listSvnFiles(url) {
    const svnUrlToUse = url ? `${svnUrl}/${url}` : svnUrl;
    const options = {
        auth: {
            username: svnUsername,
            password: svnPassword,
        },
        httpsAgent: new https.Agent({ rejectUnauthorized: false }) // 忽略 SSL 证书验证
    };

    const fetchData = async (url) => {
        try {
            console.info("Requesting SVN URL: " + url);
            const response = await axios.get(url, options);
            const html = response.data;

            // 使用正则表达式提取 <li> 标签中的名称
            const liRegex = /<li><a href="[^"]+">([^<]+)<\/a><\/li>/g;
            const files = [];
            let match;

            while ((match = liRegex.exec(html)) !== null) {
                const name = match[1].replace(/\/$/, ''); // 移除名称末尾的斜杠
                if (name !== "..") { // 排除返回上级目录的链接
                    files.push({ name });
                }
            }

            //console.info(files); // 打印提取的数据
            return files; // 返回提取的数据
        } catch (err) {
            throw new Error(`Error accessing SVN: ${err.message}`); // 抛出错误以便处理
        }
    };

    try {
        return await fetchData(svnUrlToUse); // 尝试内网地址
    } catch (err) {
        console.error(`内网访问失败: ${err.message}`);
        console.log(`尝试使用外网地址访问`);
        const remoteSvnUrlToUse = url ? `${remoteSvnUrl}/${url}` : remoteSvnUrl;
        try {
            return await fetchData(remoteSvnUrlToUse); // 尝试外网地址
        } catch (remoteErr) {
            console.error(`外网访问失败: ${remoteErr.message}`);
            throw remoteErr; // 重新抛出错误
        }
    }
}


function parseSvnData(data, resolve, reject) {
    parseString(data, (err, result) => {
        if (err) {
            console.error(`解析XML错误: ${err}`);
            reject(err);
            return;
        }
        console.log('SVN仓库内容:');
        const files = extractNamesAndDates(result.lists.list[0]);
        console.info(files);
        resolve(files);
    });
}

//提取数据
function extractNamesAndDates(data) {
    const entries = data.entry;
    const result = entries.map(entry => {
        const name = entry.name[0]; // 获取名称
        const date = entry.commit[0].date[0]; // 获取日期
        return { name, date };
    });
    return result;
}

//用svn命令实现下载
async function downloadSvnFile1(filename, targetPath) {
    return new Promise((resolve, reject) => {
        const command = `svn export --non-interactive --trust-server-cert --username ${svnUsername} --password ${svnPassword} ${svnUrl}/${filename} ${targetPath}`;
        exec(command, (error, stdout, stderr) => {
            if (error || stderr) {
                console.error(`SVN错误: ${error || stderr}`);
                // 如果使用内网地址下载失败,则尝试使用外网地址下载
                console.log(`尝试使用外网地址下载文件 ${filename}`);
                const remoteCommand = `svn export --non-interactive --trust-server-cert --username ${svnUsername} --password ${svnPassword} ${remoteSvnUrl}/${filename} ${targetPath}`;
                exec(remoteCommand, (remoteError, remoteStdout, remoteStderr) => {
                    if (remoteError || remoteStderr) {
                        console.error(`外网地址下载失败: ${remoteError || remoteStderr}`);
                        reject(remoteError || remoteStderr);
                        return;
                    }
                    console.log(`已成功下载文件 ${filename} 到 ${targetPath}`);
                    resolve();
                });
                return;
            }
            console.log(`已成功下载文件 ${filename} 到 ${targetPath}`);
            resolve();
        });
    });
}

//用node-svn-ultimate
async function downloadSvnFile2(filename, targetPath) {
    const options = {
        username: svnUsername,
        password: svnPassword,
        trustServerCert: true,
        noAuthCache: true
    };

    return new Promise((resolve, reject) => {
        const svnUrlToUse = `${svnUrl}/${filename}`;
        
        // 尝试从内网 SVN 下载
        svnUltimate.commands.export(svnUrlToUse, targetPath, options, (err) => {
            if (err) {
                console.error(`SVN内网下载错误: ${err}`);
                
                // 如果内网下载失败,尝试使用外网地址
                const remoteSvnUrlToUse = `${remoteSvnUrl}/${filename}`;
                console.log(`尝试使用外网地址下载文件 ${filename}`);
                
                svnUltimate.commands.export(remoteSvnUrlToUse, targetPath, options, (err) => {
                    if (err) {
                        console.error(`外网地址下载失败: ${err}`);
                        reject(err);
                        return;
                    }
                    console.log(`已成功使用外网地址下载文件 ${filename} 到 ${targetPath}`);
                    resolve();
                });
                return;
            }
            console.log(`已成功下载文件 ${filename} 到 ${targetPath}`);
            resolve();
        });
    });
}


async function downloadSvnFile(filename, targetPath) {
    const options = {
        auth: {
            username: svnUsername,
            password: svnPassword,
        },
        responseType: 'stream', // 以流的方式下载文件
        httpsAgent: new https.Agent({
            rejectUnauthorized: false, // 忽略 SSL 证书验证
        })
    };

    const svnUrlToUse = `${svnUrl}/${filename}`;
    
    try {
        console.log(`尝试从内网地址下载文件 ${filename}`);
        const response = await axios.get(svnUrlToUse, options);
        const writer = fs.createWriteStream(targetPath);
        
        // 将响应的文件流写入目标路径
        response.data.pipe(writer);

        // 等待写入完成
        await new Promise((resolve, reject) => {
            writer.on('finish', resolve);
            writer.on('error', reject);
        });

        console.log(`已成功从内网地址下载文件 ${filename} 到 ${targetPath}`);
    } catch (err) {
        console.error(`内网下载错误: ${err}`);
        
        // 如果内网下载失败,尝试使用外网地址
        const remoteSvnUrlToUse = `${remoteSvnUrl}/${filename}`;
        try {
            console.log(`尝试使用外网地址下载文件 ${filename}`);
            const response = await axios.get(remoteSvnUrlToUse, options);
            const writer = fs.createWriteStream(targetPath);
            
            response.data.pipe(writer);

            // 等待写入完成
            await new Promise((resolve, reject) => {
                writer.on('finish', resolve);
                writer.on('error', reject);
            });

            console.log(`已成功使用外网地址下载文件 ${filename} 到 ${targetPath}`);
        } catch (remoteErr) {
            console.error(`外网下载错误: ${remoteErr}`);
            throw remoteErr; // 抛出错误,以便外部捕获
        }
    }
}


//用svn命令下载文件夹
async function downloadSvnFolder1(dirPath,targetPath) {
    return new Promise((resolve, reject) => {
        const command = `svn checkout --non-interactive --trust-server-cert --username ${svnUsername} --password ${svnPassword} ${svnUrl}/${dirPath} ${targetPath}`;
        exec(command, (error, stdout, stderr) => {
            if (error || stderr) {
                console.error(`SVN错误: ${error || stderr}`);
                // 如果使用内网地址下载失败,则尝试使用外网地址下载
                console.log(`尝试使用外网地址下载文件夹`);
                const remoteCommand = `svn checkout --non-interactive --trust-server-cert --username ${svnUsername} --password ${svnPassword} ${remoteSvnUrl}/${dirPath} ${targetPath}`;
                exec(remoteCommand, (remoteError, remoteStdout, remoteStderr) => {
                    if (remoteError || remoteStderr) {
                        console.error(`外网地址下载失败: ${remoteError || remoteStderr}`);
                        reject(remoteError || remoteStderr);
                        return;
                    }
                    console.log(`已成功下载文件夹到 ${targetPath}`);
                    resolve();
                });
            }
            console.log(`已成功下载文件夹到 ${targetPath}`);
            resolve();
        });
    });
}

async function downloadSvnFolder2(dirPath, targetPath) {
    const options = {
        username: svnUsername,
        password: svnPassword,
        trustServerCert: true,
        noAuthCache: true
    };

    return new Promise((resolve, reject) => {
        const svnUrlToUse = `${svnUrl}/${dirPath}`;
        
        // 尝试从内网 SVN 下载文件夹
        svnUltimate.commands.checkout(svnUrlToUse, targetPath, options, (err) => {
            if (err) {
                console.error(`SVN内网下载错误: ${err}`);
                
                // 如果内网下载失败,尝试使用外网地址
                const remoteSvnUrlToUse = `${remoteSvnUrl}/${dirPath}`;
                console.log(`尝试使用外网地址下载文件夹`);
                
                svnUltimate.commands.checkout(remoteSvnUrlToUse, targetPath, options, (err) => {
                    if (err) {
                        console.error(`外网地址下载失败: ${err}`);
                        reject(err);
                        return;
                    }
                    console.log(`已成功使用外网地址下载文件夹到 ${targetPath}`);
                    resolve();
                });
                return;
            }
            console.log(`已成功下载文件夹到 ${targetPath}`);
            resolve();
        });
    });
}

async function fetchDirectoryListing(url) {
    const options = {
        auth: {
            username: svnUsername,
            password: svnPassword,
        },
        httpsAgent: new https.Agent({ rejectUnauthorized: false }),
    };

    try {
        const response = await axios.get(url, options);
        if (response.status === 200) {
            return response.data;
        } else {
            // Return an empty string for any non-200 status codes
            console.warn(`Received non-200 status code ${response.status} from ${url}`);
            return '';
        }
    } catch (error) {
        // Log the error and return an empty string instead of throwing an exception
        if (error.response) {
            // Server responded with a status other than 2xx
            console.warn(`HTTP error: ${error.response.status} - ${error.response.statusText}`);
        } else if (error.request) {
            // The request was made but no response was received
            console.warn(`No response received: ${error.request}`);
        } else {
            // Something happened in setting up the request
            console.warn(`Error in setup: ${error.message}`);
        }
        return ''; // Return an empty string when an error occurs
    }
}


// Function to parse HTML and extract file and directory names
function parseDirectoryListing(html) {
    const regex = /<li><a href="([^"]+)">([^<]+)<\/a><\/li>/g;
    const files = [];
    let match;

    while ((match = regex.exec(html)) !== null) {
        const href = match[1];
        let name = match[2].replace(/\/$/, ''); // Remove trailing slashes

        // 跳过上级目录
        if (name == '..' || name == '../') {
            continue;
        }

        files.push({ href, name });
    }

    return files;
}


// Function to download a single file
async function downloadFile(fileUrl, localPath) {
    const options = {
        auth: {
            username: svnUsername,
            password: svnPassword,
        },
        responseType: 'stream',
        httpsAgent: new https.Agent({ rejectUnauthorized: false })
    };

    const response = await axios.get(fileUrl, options);
    const writer = fs.createWriteStream(localPath);

    return new Promise((resolve, reject) => {
        response.data.pipe(writer);
        writer.on('finish', resolve);
        writer.on('error', reject);
    });
}

// Main function to download SVN folder
async function downloadSvnFolder(dirPath, targetPath) {
    let svnUrlToUse = `${svnUrl}/${dirPath}`;
    
    try {
        const html = await fetchDirectoryListing(svnUrlToUse);
        if (!html) {
            console.warn(`No HTML content returned from ${svnUrlToUse}`);
            return; // Exit early if no HTML content
        }
        const items = parseDirectoryListing(html);

        // Ensure the target path exists
        if (!fs.existsSync(targetPath)) {
            fs.mkdirSync(targetPath, { recursive: true });
        }

        // Download each item
        for (const item of items) {
            const itemUrl = `${svnUrlToUse}/${item.href}`;
            const localPath = path.join(targetPath, item.name);

            if (item.href.endsWith('/')) {
                // Recursively download directories
                await downloadSvnFolder(item.href, localPath);
            } else {
                // Download files
                await downloadFile(itemUrl, localPath);
            }
        }
        
        console.log(`已成功下载文件夹到 ${targetPath}`);
    } catch (err) {
        console.error(`下载错误: ${err}`);
        
        // Try remote SVN URL if inner network fails
        svnUrlToUse = `${remoteSvnUrl}/${dirPath}`;
        try {
            const html = await fetchDirectoryListing(svnUrlToUse);
            if (!html) {
                console.warn(`No HTML content returned from ${svnUrlToUse}`);
                return; // Exit early if no HTML content
            }
            const items = parseDirectoryListing(html);

            // Ensure the target path exists
            if (!fs.existsSync(targetPath)) {
                fs.mkdirSync(targetPath, { recursive: true });
            }

            // Download each item
            for (const item of items) {
                const itemUrl = `${svnUrlToUse}/${item.href}`;
                const localPath = path.join(targetPath, item.name);

                if (item.href.endsWith('/')) {
                    // Recursively download directories
                    await downloadSvnFolder(item.href, localPath);
                } else {
                    // Download files
                    await downloadFile(itemUrl, localPath);
                }
            }
            
            console.log(`已成功使用外网地址下载文件夹到 ${targetPath}`);
        } catch (remoteErr) {
            console.error(`外网地址下载失败: ${remoteErr}`);
            throw remoteErr; // Re-throw error after all attempts fail
        }
    }
}



module.exports = { 
    listSvnFiles,
    downloadSvnFile,
    downloadSvnFolder
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌晨两点钟同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值