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
};