• fltp备份文件后统计验证


    原文地址:https://www.cnblogs.com/jying/p/16874692.html
    作者:一定会去旅行
    欢迎任何形式的转载,但请务必在文章开始位置使用明显加粗字体注明出处。
    限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

    上一篇(https://www.cnblogs.com/jying/p/16805821.html)记录了自己在centos使用lftp备份文件的过程,本篇记录自己对备份后的文件与源文件目录的对比统计。

    三种思路:

    1、代码(如java等)循环遍历所有备份的源和ftp目标文件夹,统计个数对比。

    2、执行linux命令行统计ftp备份文件夹和本地源文件夹文件总大小写入统计文件,再利用java代码读取统计值后对比。

    3、执行linux命令行统计ftp备份文件夹和本地源文件夹文件总个数写入统计文件,再利用java代码读取统计值后对比。

    其中网上的介绍多是用的第一种方式,而且几乎全都是复制粘贴的重复垃圾文章,个人尝试可以执行,但文件夹较多和层级较多时会非常耗时(因为ftp的连接原理导致),最终放弃方式一。

    用到的方式为commons.net(org.apache.commons.net)包的ftp功能,网上的垃圾文章直接略过,直接用官方的实例测试:

    org.apache.commons.net测试实例

    其中main函数里的测试方式可以是:

        public static void test() throws UnknownHostException {
            String cmd = "-n ftp服务器ip 账号 密码 /";
            String[] cmds = cmd.split(" ");
            main(cmds);
        }

    不记得有没有参考这俩文章了:https://www.cnblogs.com/chen1281024/p/15625278.html 、https://www.cnblogs.com/leonlipfsj/p/15972372.html


    然后考虑方式2,关于linux统计目录下文件大小的命令是du,

    复制代码
    du常用的选项:
      -h:--human-readable 以人类可读的方式显示,即自动转为K,M,G等单位。
      -a:-all 显示目录占用的磁盘空间大小,含子目录和文件占用磁盘空间的大小详细列表
      -s:--summarize 显示目录占用的磁盘空间大小,不含子目录和文件占用的磁盘空间大小详细列表
      -b:-bytes 显示目录或文件大小时,以byte为单位。
      -k:--kilobytes 以KB(1024bytes)为单位输出。
      -m:--megabytes 以MB为单位输出。   
    -c:--total 显示目录或文件占用的磁盘空间大小,统计它们的总和。   --apparent-size:显示目录或文件自身的大小   -l :统计硬链接占用磁盘空间的大小   -L:统计符号链接所指向的文件占用的磁盘空间大小   du -sh : 查看当前目录总共占的容量。而不单独列出各子项占用的容量。 du -sh * | sort -n 统计当前文件夹(目录)大小,并按文件大小排序 du -lh --max-depth=1 : 查看当前目录下一级子文件和子目录占用的磁盘容量。
    复制代码

    关于以上常用选项的实例可以参考:https://blog.csdn.net/pichcar1982/article/details/121531546

    因为lftp也支持du,所以貌似可以直接通过du统计服务器本地源文件目录大小和ftp服务器文件目录大小对比就可以了,但实际执行过程发现本地文件要比同步到ftp服务器上的文件大,于是使用ls -l查看发现单个文件大小也不一致,个人猜测是服务器本地文件上传保存的字节流和ftp备份的字节流长度不一致导致的。还有一种解释(https://blog.csdn.net/mtawaken/article/details/8491413 或https://blog.csdn.net/weixin_42803243/article/details/123724755)说是服务器本身的存储块大小不一致导致的,所以du加参数--apparent-size即可,而我加上此参数发现还是不一致,即使单个文件显示一致了,整个目录的大小仍然有差异。


    方式3,对比文件个数, linux命令ls -l 可以按行列出目录下所有文件,可以直接根据行数统计出文件个数。

    复制代码
    # 查看当前目录下的文件数量(不包含子目录中的文件)
    ls -l|grep "^-"| wc -l
    
    # 查看当前目录下的文件数量(包含子目录中的文件) 注意:R,代表遍历子目录
    ls -lR|grep "^-"| wc -l
    
    # 查看当前目录下的文件夹目录个数(不包含子目录中的目录),同上,如果需要查看子目录的,加上R
    ls -l|grep "^d"| wc -l
    复制代码

    wc -l 表示统计输出信息的行数,因为经过前面的过滤已经只剩下普通文件,一个目录或文件对应一行,所以统计的信息的行数也就是目录或文件的个数。参考:https://www.cnblogs.com/wangyuxing/p/15818042.html

    在lftp中也可以使用该方式来统计文件个数,但有一些限制,比如lftp中的ls命令默认就是显示按行的文件详情,等同于普通命令ls -l,而且lftp中使用R参数无效,这意味着无法循环遍历子文件夹目录(了解过ftp的连接过程则知道在ftp里切换目录需要重新连接),所以在lftp中的统计文件个数命令写为:

    ls 文件夹目录 | grep "^-" | wc -l

    这也就意味着只能统计单层的文件夹里的文件个数。但这也是能准确统计是否备份成功的最准确方式了。

    所以我们需要对文件存储路径进行优化,优化后的存储路径应该满足最终的统计路径只有一层:

    1、存储根目录/模块/直接存储文件

    2、存储根目录/模块/年月/日/存储文件

    3、存储根目录/模块/年月日/存储文件

    4、存储根目录/年月/日/存储文件

    5、存储根目录/年月/日/模块/存储文件

    推荐使用方式2或方式5,而且所有存储格式应该统一,这样便于备份脚本只写一种遍历即可。我这边目前因为采用了多种存储方式,导致编写备份脚本时要分开写多个(参考上一篇文章)。

    一个备份和统计的实例如下:

    复制代码
    if [ -d 存储根目录/模块/年月/日 ]; then
        echo "目录存在"
        ls 存储根目录/模块/年月/日/存储文件 -l|grep "^-"| wc -l >> /本地脚本目录/bak_logs/年月/日/模块.bak.count
    lftp -u 账号,密码 ftp服务器ip << EOF
        mirror --reverse --only-missing --only-newer 存储根目录/模块/年月/日 --parallel=3 --log=/本地脚本目录/logs/年月/日/模块_小时.log
        ls 存储根目录/模块/年月/日/存储文件 |grep "^-"| wc -l >> /本地脚本目录/bak_logs/年月/日/模块.bak.count
        bye
    EOF
    else
        echo "不存在"
    fi
    复制代码

     


    有了统计个数,就可以通过代码来读取并推送邮件给管理员了。

    package test;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.io.IOException;
    import java.sql.SQLException;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.List;
    import java.util.Properties;
    
    
    public class BakFile {
        
    
        private Properties props;
        private WarningEmail warningEmail;
    
        public BakFile(Properties _props) {
            props = _props;
        }
    
        public void run() {
            try {
                // 初始化
                this.InitSetup();
    
                // 获取统计信息
                this.readResultCount();
    
            } catch (Exception exIO) {
                warningEmail.send_report_mail("核验备份文件出错啦!!!", exIO.toString());
                exIO.printStackTrace();
            }
        }
        
        private void InitSetup() throws IOException, SQLException {
            // 提醒邮件
            warningEmail = new WarningEmail(props);
        }
    
        public void readResultCount() {        
            // 获取昨天的日期,
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DATE,-1);//昨天
            String yesterday_ym = new SimpleDateFormat("yyyy-MM").format(cal.getTime());
            String yesterday_d = new SimpleDateFormat("dd").format(cal.getTime());
            String yesterday = yesterday_ym+"-"+yesterday_d;
    
            String path = "/var/jenkins_home/bak_logs/" + yesterday_ym + "/" + yesterday_d;
            File dirFile = new File(path);  
            //如果dir对应的文件不存在,或者不是一个目录,则退出  
            if (!dirFile.isDirectory()) {  
                warningEmail.send_report_mail(yesterday+"核验备份文件异常", "未获取到昨天备份目录");
                return;
            }
            //获取文件夹下所有文件 
            File[] files = dirFile.listFiles();  
            File file;
            List list;
            String content = "";
            String bodyTrContent = "";
            if(files!=null && files.length>0) {
                for (int i = 0; i < files.length; i++) {  
                    file = files[i];
                    if(file.getName().endsWith("count")) {
                        list = readFileContent(file);
                        content = setContent(file, list);
                        bodyTrContent = String.format("%s%s", bodyTrContent, content);                
                    }
                }
                content = setMailHtml(bodyTrContent, yesterday);     
                warningEmail.send_report_mail(yesterday+"文件备份情况", content);
            } else {
                warningEmail.send_report_mail(yesterday+"文件备份为空", "未获取到昨天备份目录");            
            }
        }
        
        public List readFileContent(File file) {
            BufferedReader reader = null;
            FileReader fileReader = null;
            List list = new ArrayList();
            try {
                fileReader = new FileReader(file);
                reader = new BufferedReader(fileReader);
                String tempString = null;
                // 一次读入一行,直到读入null为文件结束
                while ((tempString = reader.readLine()) != null) {
                    list.add(tempString);
    //                System.out.println(tempString);
                }
            } catch (IOException e) {
    
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e1) {
                    }
                }
                if(fileReader != null) {
                    try {
                        fileReader.close();
                    } catch (IOException e1) {
                    }
                }
            }
            return list;
        }
    
        public String setContent(File file, List list) {
            if(list.size()>=2) {
                String line1 = list.get(list.size()-2);
                String line2 = list.get(list.size()-1);
                return setTableBodyTr(file.getName().replace(".bak.count", ""), line1, line2);
            } else if(list.size()==1){ // 
                String line1 = list.get(list.size()-1);
                return setTableBodyTr(file.getName().replace(".bak.count", ""), line1, "未同步");            
            } else {
                return setTableBodyTr(file.getName().replace(".bak.count", ""), "未获取到文件夹", "未同步");            
            }
        }
        
    
        public String setMailHtml(String content, String yesterday) {
            String html = String.format("" + 
                    "  " + 
                    "       " + 
                    "         " + 
                    "      " + 
                    "   "
                    + "
    %s文件备份情况:

    " + "" + "" + "" + "" + "", yesterday); html = String.format("%s%s", html, content); html = String.format("%s
    目录本地ftp
    ", html); return html; } public String setTableBodyTr(String mName, String local, String ftp) { String content = ""; if(!local.equals(ftp)) { ftp = String.format("%s", ftp); } content = String.format("%s%s", content, mName); content = String.format("%s%s", content, local); content = String.format("%s%s", content, ftp); content = String.format("%s", content); return content; } }
    java获取统计信息并邮件推送给管理员
    package test;
    
    import java.util.Date;
    import java.util.Properties;
    
    import javax.mail.BodyPart;
    import javax.mail.Message;
    import javax.mail.Multipart;
    import javax.mail.Session;
    import javax.mail.Transport;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeBodyPart;
    import javax.mail.internet.MimeMessage;
    import javax.mail.internet.MimeMultipart;
    
    public class WarningEmail {
    
        private Properties props;
    
        public WarningEmail(Properties _props) {
            this.props = _props;
        }
    
        public void send_report_mail(String subject, String content) {
            System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    //        System.out.println(subject);
    //        System.out.println(content);
            try {
                String smtp = props.getProperty("mail.smtp");
                String user = props.getProperty("mail.user");
                String password = props.getProperty("mail.pwd");
                String from = props.getProperty("mail.from");
                String to = props.getProperty("mail.admin");
    
                Properties p = new Properties();
                p.put("mail.smtp.host", smtp);
                p.put("mail.smtp.port", "587");
                p.put("mail.smtp.auth", "true");
    
                Session ssn = Session.getDefaultInstance(p);
                Transport transport = ssn.getTransport("smtp");
                transport.connect(smtp, user, password);
    
                send_email(ssn, transport, subject, content, from, to);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private boolean send_email(Session ssn, Transport transport, String subject, String content, String from, String to) {
            try {
                BodyPart html = new MimeBodyPart();
                html.setContent(content, "text/html; charset=utf-8");
    
                Multipart mainPart = new MimeMultipart();
                mainPart.addBodyPart(html);
    
                MimeMessage message = new MimeMessage(ssn);
                message.setContent(mainPart);
    
                message.setFrom(new InternetAddress(from));
                message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
    //            message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse("抄送人邮箱")); // 密送人
                message.setSubject(subject);
                message.setContent(mainPart);
                message.setSentDate(new Date());
    
                transport.sendMessage(message, message.getAllRecipients());
                System.out.println(String.format("%s : %s", to, subject));
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    }
    发送邮件

     

    生活不止眼前的苟且,还有诗和远方~~
    原文地址:https://www.cnblogs.com/jying/p/16874692.html
    作者:一定会去旅行
    欢迎任何形式的转载,但请务必在文章开始位置使用明显加粗字体注明出处。
    限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

  • 相关阅读:
    玩客云 线刷Armbian 搭配Alist 阿里云盘 Jellyfin NovaVideoPlayer搞电视墙
    网络协议详解:TCP Part1
    Xilinx约束学习笔记—— 时序约束
    【C++初阶】小白入门C++
    tensorrt 高级(1):完整的分类器实现
    三. Docker的安装Mysql并挂载
    【CTF】AWDP总结(Web)
    道可云元宇宙每日资讯|5G数智新时代元宇宙发展论坛在厦门举办
    系列文章|云原生时代下微服务架构进阶之路 - Snap-E
    Python + Django4 搭建个人博客(二):准备开发环境
  • 原文地址:https://www.cnblogs.com/jying/p/16874692.html