又遇到了一个功能点,网上的参考内容挺少的,终于把功能实现了,记一下,方便以后遇到坑的时候看看
请不要在意代码中的dto类
* @Author: winterchen
* @Description: 获取SVN目录下下一层所有文件信息
* @Date: 2018/2/6
* @param dirPath 需要查找的目录名
* @param fileNameSearch 名称模糊搜索
* @param typeEnum 文件的类型枚举 -- 这个就是三个参数 ‘dir’,‘file’,‘all’
*/
public List getNextLvFileList2(String dirPath, String fileNameSearch, SvnGetFileTypeEnum typeEnum){
if (dirPath.startsWith("/")){
dirPath = dirPath.replaceFirst("/", "");
}
try {
//获取SVNRepository
SVNRepository svnRepository = SVNUtil.getSVNRepository();
//获取路径下的所有文件夹或文件
List svnList = SvnOption.getSvnFileBaseInfoDto(svnRepository,dirPath, fileNameSearch, typeEnum,
false);
//这个方法只是做对象转换的,忽略
return parsePmcFileInfoDtos(svnList);
} catch (SVNException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.GET_FILE_LIST_FAIL)
.withErrorMessageArguments(e.getMessage());
}
}
SVNUtil.java
public class SVNUtil {
//svn连接地址
public static String storeUrl=null;
private static final String USERNAME = "你的账号";
private static final String PASSWD = "你的密码";
static{
storeUrl = “https://ip:port/svn/...”
}
/** * @Author: winterchen * @Description: 获取SVNRepository * @Date: 2018/2/6 * @param url */
public static SVNRepository getSVNRepository(String url) throws SVNException{
SVNRepository repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager( USERNAME , PASSWD );
repository.setAuthenticationManager( authManager );
return repository;
}
/** * @Author: winterchen * @Description: 获取SVNRepository * @Date: 2018/2/7 * @param */
public static SVNRepository getSVNRepository() throws SVNException{
return SVNUtil.getSVNRepository(storeUrl);
}
}
/** * @Author: winterchen * @Description: 获取 SVNUpdateClient * @Date: 2018/2/9 * @param */
public static SVNUpdateClient getSVNUpdateClient(){
//声明SVN客户端管理类
SVNClientManager ourClientManager = null;
//初始化库。 必须先执行此操作。具体操作封装在setupLibrary方法中。
setupLibrary();
ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
//实例化客户端管理类
ourClientManager = SVNClientManager.newInstance(
(DefaultSVNOptions) options, USERNAME, PASSWD);
//通过客户端管理类获得updateClient类的实例。
SVNUpdateClient updateClient = ourClientManager.getUpdateClient();
updateClient.setIgnoreExternals(false);
return updateClient;
}
/* * 初始化库 */
private static void setupLibrary() {
/* * For using over http:// and https:// */
DAVRepositoryFactory.setup();
/* * For using over svn:// and svn+xxx:// */
SVNRepositoryFactoryImpl.setup();
/* * For using over file:/// */
FSRepositoryFactory.setup();
}
/** * @Author: winterchen * @Description: 获取 SVNClientManager * @Date: 2018/2/9 * @param */
public static SVNClientManager getSVNClientManager(){
DAVRepositoryFactory.setup();
ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
//实例化客户端管理类
return SVNClientManager.newInstance((DefaultSVNOptions) options, USERNAME, PASSWD);
}
SvnOption.java
/** * @Author: winterchen * @Description: 获取版本库中某一目录下的所有条目。可以根据参数选择是否进行递归 * @Date: 2018/2/6 * @param repository * @param path 需要查找的svn路径(相对路径)前面不需要/ * @param typeEnum 文件类型 * @param fileNameSearch 文件名过滤条件 * @param isGetNext 是否递归 true 递归 */
public static List getSvnFileBaseInfoDto(SVNRepository repository, String path,
String fileNameSearch, SvnGetFileTypeEnum typeEnum,
boolean isGetNext)throws SVNException{
List result = new LinkedList<>();
listEntries(repository,path,result,fileNameSearch,typeEnum,isGetNext);
return result;
}
/** * @Author: winterchen * @Description: 获取版本库中某一目录下的所有条目。可以根据参数选择是否进行递归 * @Date: 2018/2/6 * @param repository * @param path 需要查找的svn路径(相对路径)前面不需要/ * @param result 最后封装的容器 * @param typeEnum 文件类型 * @param fileNameSearch 文件名过滤条件 * @param isGetNext 是否递归 true 递归 */
public static void listEntries(SVNRepository repository, String path, List result,
String fileNameSearch, SvnGetFileTypeEnum typeEnum,
boolean isGetNext) throws SVNException {
//获取版本库的path目录下的所有条目。参数-1表示是最新版本。
Collection entries = repository.getDir(path, -1, null,
(Collection) null);
Iterator iterator = entries.iterator();
while (iterator.hasNext()) {
SVNDirEntry entry = (SVNDirEntry) iterator.next();
boolean flag = true;
//进行参数的匹配
if (typeEnum != null) {
if (!typeEnum.getValue().equals(SvnGetFileTypeEnum.DEFAULT.getValue())) {
if (!entry.getKind().toString().equals(typeEnum.getValue())) {
flag = false;
}
}
}
if (!StringUtils.isEmpty(fileNameSearch)) {
if (!entry.getName().contains(fileNameSearch)) {
flag = false;
}
}
if (flag) { //如果两个条件都不为空,并且都通过了
result.add(entry);
}
//判断是否需要进行递归
if (isGetNext) {
/* * 检查此条目是否为目录,如果为目录递归执行 */
if (entry.getKind() == SVNNodeKind.DIR) {
listEntries(repository, (path.equals("")) ? entry.getName()
: path + "/" + entry.getName(), result, fileNameSearch, typeEnum, isGetNext);
}
}
}
}
controller
方法是通过将输出流通过HttpServletResponse传给前端进行下载
@ApiOperation("SVN文件的下载")
@RequestMapping(value = "/svn/download", method = RequestMethod.GET)
public APIResponse downloadFileBy(
@ApiParam(name = "path", value = "需要下载文件的相对路径", required = true)
@RequestParam(name = "path", required = true)
String path,
HttpServletResponse response){
SVNProperties svnProperties = new SVNProperties();
BufferedOutputStream bos = null;
//文件名称
try {
if (path.startsWith("/")){
path = path.replaceFirst("/", "");
}
String filename = path.substring(path.lastIndexOf("/") + 1);
String url = path.substring(0,path.lastIndexOf("/"));
filename = URLEncoder.encode(filename,"UTF-8");
response.reset();
response.setContentType("application/force-download");// 设置强制下载不打开
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition",
"attachment;fileName=" + filename);// 设置文件名
bos = new BufferedOutputStream(response.getOutputStream());
SVNRepository svnRepository = SVNUtil.getSVNRepository(SVNUtil.storeUrl + "/" + url);
svnHandleService.getSvnFileOutputstream(svnRepository,bos,svnProperties,path);
response.flushBuffer();
bos.close();
} catch (SVNException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments(e.getMessage());
} catch (IOException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments(e.getMessage());
}
return APIResponse.success();
}
/** * @Author: winterchen * @Description: 获取SVN文件的输出流和文件的属性 * @Date: 2018/2/7 * @param repository * @param outputStream 输出流 * @param fileProperties 文件属性 * @param path 文件的相对路径 前面不带/ */
public void getSvnFileOutputstream(SVNRepository repository, OutputStream outputStream,
SVNProperties fileProperties, String path){
if (repository == null || outputStream == null || fileProperties == null || StringUtils.isEmpty(path))
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments("参数不能为空");
try {
SvnOption.getSvnFileOutputStream(repository,outputStream,fileProperties,path);
} catch (SVNException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments(e.getMessage());
}
}
/** * @Author: winterchen * @Description: 获取SVN文件的输出流和文件的属性 * @Date: 2018/2/7 * @param repository * @param outputStream 输出流 * @param fileProperties 文件属性 * @param path 文件的相对路径 前面不带/ */
public static void getSvnFileOutputStream(SVNRepository repository, OutputStream outputStream,
SVNProperties fileProperties, String path) throws SVNException {
//进行path的处理
String url = path.substring(0,path.lastIndexOf("/"));
String filePath = path.substring(path.lastIndexOf("/") + 1);
try {
//获得版本库中文件的类型状态(是否存在、是目录还是文件),参数-1表示是版本库中的最新版本。
SVNNodeKind nodeKind = repository.checkPath(filePath, -1);
if (nodeKind == SVNNodeKind.NONE) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments("要查看的文件在 '" + url + "'中不存在");
} else if (nodeKind == SVNNodeKind.DIR) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments("要查看对应版本的条目在 '" + url
+ "'中是一个目录.");
}
//获取要查看文件的内容和属性,结果保存在baos和fileProperties变量中。
repository.getFile(filePath, -1, fileProperties, outputStream);
} catch (SVNException svne) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.DOWNLOAD_FILE_FAIL)
.withErrorMessageArguments(svne.getMessage());
}
}
将不同级别的目录中的文件目录(内含子类)copy到一个指定的文件夹中,请看注释中的实现思路就知道了
/** * @Author: winterchen * @Description: 创建项目Svn目录(并加入模板文件) * @Date: 2018/2/9 * @param path * @param projId */
public void addNewProjSvnDir(String path, Integer projId) throws SVNException {
DataDictDomain projDict = dataDictDao.getDataDictByProjId(projId);
/** 实现思路: * 根据项目的类型找到相应的svn目录,docheckout到本地 * 然后doimport到刚刚创建的文件夹中 * finally 删除本地文件夹 **/
//将模板文件夹checkOut到本地
File wcDir = new File(SvnOption.getSysLocalDir(projId.toString()));
try {
//将svn的模板目录checkout到本地
SvnOption.checkOut(wcDir,SVNUtil.storeUrl + "/PMCLIB/" + projDict.getEntryName() + "项目");
//创建svn目录
SvnOption.addNewSvnDir(path, projDict.getEntryName());
//将本地的文件目录import到svn
SvnOption.imoprtToSvnDir(wcDir,SVNUtil.storeUrl + "/" + path);
} catch (Exception e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments(e.getMessage());
}finally {
FileUtil.deleteDir(wcDir);
}
}
SvnOption.java
/** * @Author: winterchen * @Description: 往SVN中添加文件夹 * @Date: 2018/2/9 * @param path * @param projTypeName */
public static void addNewSvnDir(String path, String projTypeName) throws SVNException {
String filename = path.substring(path.lastIndexOf("/") + 1);
String url = SVNUtil.storeUrl + "/" + path.substring(0,path.lastIndexOf("/"));
//模板文件所在目录
String copyPath = "svn/" + "PMCLIB/" + projTypeName + "项目";
SVNRepository repository = null;
try{
repository = SVNUtil.getSVNRepository(url);
}catch (SVNException e){
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments("相对路径错误,请检查后重试");
}
SVNNodeKind nodeKind = repository.checkPath(filename, -1);
//获得版本库中文件的状态(是否存在),参数-1表示是版本库中的最新版本。
ISVNEditor editor = repository.getCommitEditor("logMessage", null,true,null);
if (nodeKind == SVNNodeKind.NONE){//如果文件夹不存在,创建一个
editor.openRoot(-1);
editor.addDir(filename,null, -1);
editor.closeDir();
editor.closeEdit();
}else {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments("文件夹已经存在");
}
}
/** * @Author: winterchen * @Description: SVN checkOut到本地 * @Date: 2018/2/9 * @param wcDir 需要checkOut到本地的文件目录 * @param rlUrl */
public static void checkOut(File wcDir, String url) throws BusinessException{
SVNUpdateClient svnUpdateClient = SVNUtil.getSVNUpdateClient();
//相关变量赋值
SVNURL repositoryURL = null;
//执行check out 操作,返回工作副本的版本号。
long workingVersion = -1;
try {
repositoryURL = SVNURL.parseURIEncoded(url);
workingVersion = svnUpdateClient
.doCheckout(repositoryURL, wcDir, SVNRevision.HEAD, SVNRevision.HEAD, SVNDepth.INFINITY,false);
} catch (SVNException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments(e.getMessage());
}catch (Exception e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments(e.getMessage());
}
}
/** * @Author: winterchen * @Description: import本地文件目录(包括其下的所有子目录和文件)到svn * @Date: 2018/2/9 * @param localFile * @param url */
public static void imoprtToSvnDir(File localFile, String url) throws BusinessException{
SVNClientManager svnClientManager = SVNUtil.getSVNClientManager();
//执行导入操作
SVNCommitInfo commitInfo = null;
SVNURL repositoryURL = null;
try {
repositoryURL = SVNURL.parseURIEncoded(url);
commitInfo = svnClientManager.getCommitClient().doImport(localFile, repositoryURL,
"import operation!",null, false,false,SVNDepth.INFINITY);
} catch (SVNException e) {
throw BusinessException.withErrorCode(ErrorsWepm.JiraAndSvnIntf.CREATE_SVN_DIR_FAIL)
.withErrorMessageArguments(e.getMessage());
}
}
/** * @Author: winterchen * @Description: 获取系统临时目录地址 * @Date: 2018/2/9 * @param dirName */
public static String getSysLocalDir(String dirName){
// 获取系统临时文件目录(兼容Windows和Linux)+PMC+时间戳+用户账号作为临时检出目录
String localCheckDir = System.getProperty(tmpdir_property) + "PMC"
+ new SimpleDateFormat(time_sdf).format(new Date()) + "_" + dirName;
localCheckDir = FilePathUtil.getHttpURLPath(localCheckDir); // 转换地址格式:主要目录分隔符都转为正斜杠/
return localCheckDir;
}
api文档:https://svnkit.com/javadoc/index.html
工作中遇到这些需求,在这些需求中不断找寻答案,然后成长。