文档章节

APDPlat中备份文件异地容灾机制之FTP上传

杨尚川
 杨尚川
发布于 2014/02/01 19:16
字数 1275
阅读 216
收藏 1

APDPlat在数据库备份成功之后,会调用一个系统扩展点,用户可以方便地编写自己的包含特定业务逻辑的插件,并可配置启用哪些插件。本文以将备份文件上传到FTP服务器以实现异地容灾为例子,来说明如何编写自己的插件并配置使其生效。

 

1、如何编写?

 

我们先看看系统扩展点,即BackupFileSender接口:

 

/**
 * 备份文件发送器
 * 将最新的备份文件发送到其他机器,防止服务器故障丢失数据
 * @author 杨尚川
 */
public interface BackupFileSender {
    public void send(File file);
}

 

 

然后新建一个类FtpBackupFileSender,实现系统扩展点:

 

/**
 * 将备份文件发送到FTP服务器上面
 * @author 杨尚川
 */
@Service
public class FtpBackupFileSender implements BackupFileSender{
    protected final APDPlatLogger LOG = new APDPlatLogger(getClass());
    
    @Resource(name="ftpUtils")
    private FtpUtils ftpUtils;
    
    @Resource(name="configurationEncryptor")
    private StandardPBEStringEncryptor configurationEncryptor;
    
    @Override
    public void send(File file) {
        try{
            String host = PropertyHolder.getProperty("ftp.server.host");
            int port = PropertyHolder.getIntProperty("ftp.server.port");
            String username = PropertyHolder.getProperty("ftp.server.username");
            String password = PropertyHolder.getProperty("ftp.server.password");
            if(username!=null && username.contains("ENC(") && username.contains(")")){
                username=username.substring(4,username.length()-1);
            }
            if(password!=null && password.contains("ENC(") && password.contains(")")){
                password=password.substring(4,password.length()-1);
            }        
            username = configurationEncryptor.decrypt(username);
            password = configurationEncryptor.decrypt(password);
            String dist = PropertyHolder.getProperty("log.backup.file.ftp.dir");
            String database = PropertyHolder.getProperty("jpa.database");
            dist = dist.replace("${database}", database);
            LOG.info("本地备份文件:"+file.getAbsolutePath());
            LOG.info("FTP服务器目标目录:"+dist);
            boolean connect = ftpUtils.connect(host, port, username, password);
            if(connect){
                boolean result = ftpUtils.uploadTo(file, dist);
                if(result){
                    LOG.info("备份文件上传到FTP服务器成功");
                }else{
                    LOG.error("备份文件上传到FTP服务器失败");
                }
            }
        }catch(Exception e){
            LOG.error("备份文件上传到FTP服务器失败",e);
        }
    }
}

 

 

这里有三个要点一是跟FTP服务器相关的信息(主机、端口、用户名、密码)从配置文件config.properties或config.local.properties中获取;使用FtpUtils的uploadTo方法实现实际的文件上传功能;三是使用StandardPBEStringEncryptor对加密的用户名和密码进行解密(配置文件中不存放明文用户名和密码)。

 

先看看FTP服务器的配置信息,默认存放在config.properties中,用户可以在config.local.properties中进行重新指定以覆盖默认配置:

 

ftp.server.host=192.168.0.100
ftp.server.port=21
ftp.server.username=ENC(rAjYIMF6ANd2q/cTgX6SpQ==)
ftp.server.password=ENC(GHVWGhan3XajaRZF8QzZKQ==)

 

 

接下来看看FtpUtils的实现:

 

/**
 * FTP操作工具
 * @author 杨尚川
 */
@Service
public class FtpUtils {
    protected final APDPlatLogger LOG = new APDPlatLogger(getClass());

    private FTPClient ftpClient;

    /**
     * 连接FTP服务器
     * @param host FTP服务器地址
     * @param port FTP服务器端口号
     * @param username 用户名
     * @param password 密码
     * @return
     */
    public boolean connect(String host, int port, String username, String password) {
        try{
            ftpClient = new FTPClient();
            ftpClient.connect(host, port);
            ftpClient.login(username, password);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                LOG.error("连接FTP服务器失败,响应码:"+reply);
                return false;
            }
        }catch(IOException e){
            LOG.error("连接FTP服务器失败",e);
            return false;
        }
        return true;
    }
    /**
     * 上传文件到服务器上的特定路径
     * @param file 上传的文件或文件夹
     * @param path 上传到ftp服务器哪个路径下
     * @return 
     */
    public boolean uploadTo(File file, String path){
        //切换到服务器上面的合适目录
        //如果对应的目录不存在,则创建
        LOG.info("上传文件 "+file.getAbsolutePath()+" 到服务器路径 "+path);
        try{
            String[] segs = path.split("/");
            for(String seg : segs){
                if(!ftpClient.changeWorkingDirectory(seg)){
                    ftpClient.makeDirectory(seg);
                    if(!ftpClient.changeWorkingDirectory(seg)){
                        LOG.error("服务器目录切换错误:"+seg);
                        return false;
                    }
                }
            }
        }catch(IOException e){
            LOG.error("服务器目录切换错误",e);
            return false;
        }
        return upload(file);
    }
    /**
     * 上传文件
     * @param file 上传的文件或文件夹
     * @return 是否上次成功
     */
    private boolean upload(File file) {
        try{
            if (file.isDirectory()) {
                ftpClient.makeDirectory(file.getName());
                ftpClient.changeWorkingDirectory(file.getName());
                File[] subFiles = file.listFiles();
                for (File subFile : subFiles) {
                    if (subFile.isDirectory()) {
                        upload(subFile);
                        ftpClient.changeToParentDirectory();
                    } else {
                        try (FileInputStream input = new FileInputStream(subFile)) {
                            ftpClient.storeFile(subFile.getName(), input);
                        }
                    }
                }
            } else {
                try (FileInputStream input = new FileInputStream(file)) {
                    ftpClient.storeFile(file.getName(), input);
                }
            }
        }catch(IOException e){
            LOG.error("上传文件失败",e);
            return false;
        }
        return true;
    }
}

 

 

最后看看解密,StandardPBEStringEncryptor是定义在Spring的配置文件中的,默认加密和解密的密码是config:

 

<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
	<property name="config" ref="environmentVariablesConfiguration" />
</bean>

<bean id="environmentVariablesConfiguration" class="org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig">
	<property name="algorithm" value="PBEWithMD5AndDES" />
	<property name="password" value="config" />
</bean>

 

 

那么如何对明文的用户名和密码进行加密,然后放入到配置文件中呢?

 

可以使用APDPlat_Web子项目中的util.ConfigEncryptUtils类来实现:

 

/**
 *把密文放到配置文件中的时候要注意:
 * ENC(密文)
 * @author 杨尚川
 */
public class ConfigEncryptUtils {
    private static final StandardPBEStringEncryptor ENCRYPTOR = new StandardPBEStringEncryptor();
    static{
        EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
        config.setAlgorithm("PBEWithMD5AndDES");
        //自己在用的时候更改此密码
        config.setPassword("config");        
        
        ENCRYPTOR.setConfig(config);
    }
    public static void main(String[] args){
        String plaintext="test";
        String ciphertext=ENCRYPTOR.encrypt(plaintext);
        System.out.println(plaintext+" : "+ciphertext);
    }
}

 

 

在main方法中指定plaintext的值为待加密的字符串,然后运行ConfigEncryptUtils类,控制台输出如下:

 

test : pXnEaEXPoseN4lSNTuz3eQ==

 

把密文放到配置文件中的时候要注意,在密文前加入ENC(,在密文后加入),形如ENC(密文),如下所示:

 

ftp.server.username=ENC(rAjYIMF6ANd2q/cTgX6SpQ==)
ftp.server.password=ENC(pXnEaEXPoseN4lSNTuz3eQ==)

 

 

2、如何配置?

 

插件编写完成之后,如何配置使其生效?

 

config.local.properties中指定log.backup.file.sender的值为所编写的插件的Spring bean name,可指定多个,用;分割,如下所示:

 

log.backup.file.sender=ftpBackupFileSender;

 

 

 

APDPlat托管在Github

 

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1103
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
APDPlat中数据库备份恢复的设计与实现

APDPlat提供了web接口的数据库备份与恢复,支持手工操作和定时调度,可下载备份文件到本地,也可把备份文件发送到异地容错,极大地简化了数据库的维护工作。 设计目标: 1、多数据库支持 2、...

杨尚川
2014/01/30
245
0
基于word分词提供的文本相似度算法来实现通用的网页相似度检测

实现代码:基于word分词提供的文本相似度算法来实现通用的网页相似度检测 运行结果: 检查的博文数:128 1、检查博文:192本软件著作用词分析(五)用词最复杂99级,相似度分值:Simple=0.96...

杨尚川
2015/05/28
1K
0
APDPlat如何自动建库建表并初始化数据?

APDPlat共支持10种数据库:DB2、DERBY、H2、HSQL、INFORMIX、MYSQL、ORACLE、POSTGRESQL、SQLSERVER、SYBASE。 数据库的默认配置信息在文件APDPlatCore/src/main/resources/org/apdplat/db.p...

杨尚川
2014/02/08
360
0
应用级产品开发平台 - APDPlat

APDPlat快速体验 APDPlat入门指南 APDPlat专题文章 APDPlat是Application Product Development Platform(应用级产品开发平台)的缩写。 APDPlat提供了应用容器、多模块架构、代码生成、安装...

杨尚川
2012/10/30
6.7K
0
APDPlat的系统启动和关闭流程剖析

APDPlat接管了Spring的启动关闭权,为各种运行其上的开源框架和类库的无缝集成提供了支持。 当然,大家都知道,一个JAVA EE Web应用的入口点是web.xml,APDPlat当然也不例外,我们看看APDPl...

杨尚川
2014/02/03
414
0

没有更多内容

加载失败,请刷新页面

加载更多

从AnnotationTransactionAspect开始rushSpring事务

0. Spring 事务 with LTW 0.1. Spring 事务 With LTW的原因: Pure Proxy-base mode有缺陷,其失效原因分析及使用方法及运行机制(LoadTimeWeaverBeanDefinitionParser和 AspectJWeavingEnable......

Aruforce
12分钟前
2
0
mac 安装protobuf 2.5.0

下载安装包 目前protobuf的最新版本是3.9.1,但是hadoop等好多框架依然依赖的是2.5.0,因此,最好不要安装最新的。 https://github.com/protocolbuffers/protobuf/releases/tag/v2.5.0 编译安...

hexiaoming123
16分钟前
3
0
Qt编写自定义控件52-颜色下拉框

一、前言 这个控件写了很久了,元老级别的控件之一,开发之初主要是自己的好几个项目要用到,比如提供一个颜色下拉框设置对应的曲线或者时间颜色,视频监控项目中经常用到的OSD标签设置,这个...

飞扬青云
24分钟前
2
0
Shell脚本应用 – for、while循环语句

通过Shell脚本应用(二)学习到了if条件条件语句的使用方法等。Shell作为一种脚本编程语言,同样了包含了循环,分支等其他程序控制结构,从而能够轻松完成更加复杂、强大的功能。我们今天就来...

Linux就该这么学
29分钟前
3
0
Sqoop之导入到Hive时特殊字符导致数据变乱

问题是这样的: Sqoop从关系型数据库导入数据到Hive时,发现数据量增多了,查找之后发现是由于源数据中含义\r\t\n特殊字符的数据,这样Hive遇到之后就将其视为换行,所以导入到Hive后数据条数...

克虏伯
31分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部