zoukankan      html  css  js  c++  java
  • 基于MFT文件上传和下载

    1. MFT介绍

    Managed File Transfer (“MFT”)是一种安全的数据传输软件,是通过网络从一台计算机到另一台计算机的数据传输。

    大文件传输(MFT)是一种安全的数据传输软件,是通过网络从一台计算机到另一台计算机的数据传输。MFT软件通常基于FTP网路协议。而且,MFT也可弥补FTP的缺陷。

    2. 业务背景

    2.1 业务流程

    做的项目需要与A银行系统对接,在文件传输这块,A银行用的是MFT这种方式,并且做了一定程度的封装。大致流程是:

    业务流程

    2.2 文件上传

    1. 我们需要在我们服务器上搭建MFT的客户端,A银行会给我们分配一个 USERID ,这个UESRID会指向A银行在自己MFT服务器上为我们分配的文件目录;

    2. 在本地服务器上创建上传目录,将要上传的文件放在该目录下;

    3. 执行A银行提供的shell脚本,上传脚本内容如下

      /home/Axway/Synchrony/SecureClient/sclient script /home/Axway/Synchrony/SecureClient/put
      
      • 1

      前面脚本都是固定的(MFT客户端安装目录,可配置成环境变量),核心在于put文件(路径可自定义),put文件示例:

      open ABank
      lcd /home/qy_work/cebfile/upload
      cd USERID
      newjob
      put 20181107.txt
      jobsubmit
      close
      

      其中 /home/qy_work/cebfile/upload 是文件上传目录,USERID为银行分配的唯一标识,20181107.txt是需要上传的文件,这里可以上传多个文件;

    4. 上传后返回信息:

      Checking transfer engine status on port 1717: is running
      open cebbank
      Connected
      lchdir /home/qy_work/cebfile/upload
      current working directory: /home/qy_work/cebfile/upload
      chdir USERID
      Directory changed
      newjob
      put 20181107.txt
      inserted in current job
      jobsubmit
      Job submitted: 20
      Close
      Disconnected
      

      这里需要注意的是Job submitted: 20,这里称为JOBID,用来查询上传状态;

    5. 查询上传文件状态信息:

      /home/Axway/Synchrony/SecureClient/sclientadm displayjob -id JOBID
      
      • 1
    6. 查询上传文件状态返回信息:

      Ident                            = 20
      Description                      = 
      Site Alias                       = ABank
      Creation Date                    = 2018/11/07 16:25:59
      Start Date                       = 2018/11/07 16:25:59
      End Date                         = 2018/11/07 16:25:59
      Status                           = Finished
      Error Message                    = 
      Percent                          = 100 %
      Total size                       = 20 B (20 bytes)
      Tasks count                      = 1
      Current Task                     = 0
      Connection Retry Count           = 0
      Transfer Retry Count             = 0
      Stop On Error                    = false
      Update Frequency                 = 5000
      

      重点在于 statuspercent两个字段对应信息,前者标识文件上传状态,后者标识文件上传百分比。

      至此,基于MFT文件上传的流程全部描述完。接下来说说文件下载的流程;

    2.3 文件下载

    1. 文件下载同样也需要执行A银行提供的脚本,下载脚本如下:

      /home/Axway/Synchrony/SecureClient/sclient script /home/Axway/Synchrony/SecureClient/get
      
      • 1

      前面脚本都是固定的(MFT客户端安装目录,可配置成环境变量),核心在于get文件(路径可自定义),get文件示例:

      open cebbank
      lcd /home/qy_work/cebfile/download
      cd USERID
      newjob
      get 2018110702.txt
      jobsubmit
      close
      

      可以发现,和put脚本区别主要在于putget命令区别,其中 /home/qy_work/cebfile/download是文件下载目录,USERID为银行分配的唯一标识,20181107.txt是需要下载的文件,这里可以下载多个文件;

    2. 下载后返回信息:

      Checking transfer engine status on port 1717: is running
      open ABank
      Connected
      lchdir /home/qy_work/cebfile/download
      current working directory: /home/qy_work/cebfile/download
      chdir USERID
      Directory changed
      newjob
      get 2018110702.txt
      inserted in current job
      jobsubmit
      Job submitted: 23
      Close
      Disconnected
      

      这里需要注意的是Job submitted: 20,这里称为JOBID,用来查询下载状态;

    3. 查询上传文件状态信息:

      /home/Axway/Synchrony/SecureClient/sclientadm displayjob -id JOBID
      
      • 1
    4. 查询上传下载状态返回信息:

      Ident                            = 23
      Description                      = 
      Site Alias                       = ABank
      Creation Date                    = 2018/11/07 17:30:14
      Start Date                       = 2018/11/07 17:30:14
      End Date                         = 2018/11/07 17:30:14
      Status                           = Finished
      Error Message                    = 
      Percent                          = 100 %
      Total size                       = 20 B (20 bytes)
      Tasks count                      = 1
      Current Task                     = 0
      Connection Retry Count           = 0
      Transfer Retry Count             = 0
      Stop On Error                    = false
      Update Frequency                 = 5000
      

      重点在于 statuspercent两个字段对应信息,前者标识文件下载状态,后者标识文件下载百分比。

      到这里整个文件上传和下载的流程以及整个上传下载的过程已经全部走通。但是这是基于Linux下执行脚本来上传和下载文件。现在需要把这些过程转换成java代码。

    3. 业务难点

    • put、get脚本不是固定的,需要传入文件路径、文件名称以及用户ID,其中用户ID可能是唯一的,文件路径和文件名称需要灵活配置;
    • java操作shell脚本;
    • java接收shell脚本执行后的流,并转成字符流,截取相应的上传、下载状态来判定文件上传、下载的最终状态;

    4. 技术方案

    4.1 脚本生成

    对于put、get脚本生成,这里采用静态模板Velocity技术,动态生成put/get脚本。

    4.1.1 添加依赖

     <!-- 模板引擎 -->
     <dependency>
       <groupId>org.apache.velocity</groupId>
       <artifactId>velocity-engine-core</artifactId>
       <version>2.0</version>
     </dependency>
    

    4.1.2 脚本模板

    MFTModel.vm

    open cebbank
    lcd $!{dirPath}
    cd $!{userId}
    newjob
    $!{manageType} $!{fileName}
    jobsubmit
    close
    

    4.1.3 生成脚本的工具类

    package com.ceb.mental.util;
    
    import org.apache.velocity.Template;
    import org.apache.velocity.VelocityContext;
    import org.apache.velocity.app.VelocityEngine;
    import org.apache.velocity.runtime.RuntimeConstants;
    import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
    
    import java.io.*;
    
    
    public class VelocityUtil {
        private final static String CHARSET = "utf8";
    
        /**
         * 生成MFT 上传、下载脚本
         *
         * @param manageType 上传或下载 put/get
         * @param dirPath    上传文件所在文件夹或下载文件存放文件夹
         * @param fileName   上传文件名称或下载文件名称
         * @param dest       生成脚本存放路径(文件夹+文件名称)
         * @param userId     银行分配的id
         * @param encode     编码格式
         */
        public static String createMFTCommandFile(String manageType,
                                                  String dirPath,
                                                  String fileName,
                                                  String dest,
                                                  String userId,
                                                  String encode) {
            FileOutputStream outStream = null;
            BufferedWriter sw = null;
            try {
                VelocityEngine ve = new VelocityEngine();
                ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
                ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
                ve.init();
                Template t = ve.getTemplate("MFTModel.vm", encode);
                VelocityContext ctx = new VelocityContext();
                ctx.put("dirPath", dirPath);
                ctx.put("userId", userId);
                ctx.put("fileName", fileName);
                ctx.put("manageType", manageType);
                //确定静态文档在共享文件目录的完整存储路径
                File file = new File(dest);
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                outStream = new FileOutputStream(file);
                OutputStreamWriter oswriter = new OutputStreamWriter(outStream, encode);
                sw = new BufferedWriter(oswriter);
                t.merge(ctx, sw);
                sw.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (sw != null) {
                        sw.close();
                    }
                    if (outStream != null) {
                        outStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return dest;
        }
    
        /**
         * 生成MFT 上传脚本
         *
         * @param manageType 上传或下载 put/get
         * @param dirPath    上传文件所在文件夹或下载文件存放文件夹
         * @param fileName   上传文件名称或下载文件名称
         * @param dest       生成脚本存放路径(文件夹+文件名称)
         * @param userId     银行分配的id
         */
        public static String createMFTCommandFile(String manageType,
                                                  String dirPath,
                                                  String fileName,
                                                  String dest,
                                                  String userId) {
            return createMFTCommandFile(manageType, dirPath, fileName, dest, userId, CHARSET);
        }
    
        public static void main(String[] args) throws IOException {
            System.out.println(createMFTCommandFile("put", "/home/qy_work/cebfile/upload",
                    "20181107.txt", "D:/file/put01", "USERID"));
        }
    }
    
    

    4.2 执行脚本

    4.2.1 java执行Shell命令介绍

    每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。可以通过getRuntime方法获取当前运行时环境。 java执行shell命令介绍
    应用程序不能创建自己的Runtime类实例。
    介绍几个主要方法:

    • Process exec(String command)
      ​ 在单独的进程中执行指定的字符串命令。
    • Process exec(String command, String[] envp)
      ​ 在指定环境的单独进程中执行指定的字符串命令。
    • Process exec(String command, String[] envp, File dir)
      ​ 在有指定环境和工作目录的独立进程中执行指定的字符串命令。
    • Process exec(String[] cmdarray)
      ​ 在单独的进程中执行指定命令和变量。
    • Process exec(String[] cmdarray, String[] envp)
      ​ 在指定环境的独立进程中执行指定命令和变量。
    • Process exec(String[] cmdarray, String[] envp, File dir)
      ​ 在指定环境和工作目录的独立进程中执行指定的命令和变量。
      command:一条指定的系统命令。
      envp:环境变量字符串数组,其中每个环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为null。
      dir:子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null。
      cmdarray:包含所调用命令及其参数的数组。

    4.2.2 执行shell命令后会返回inputStream

    例如:

    public class Test {  
        public static void main(String[] args){  
            InputStream in = null;  
            try {  
                Process pro = Runtime.getRuntime().exec(new String[]{"sh",  
                                         "/home/test/test.sh","select admin from M_ADMIN",  
                                         "/home/test/result.txt"});  
                pro.waitFor();  
                in = pro.getInputStream();  
                BufferedReader read = new BufferedReader(new InputStreamReader(in));  
                String result = read.readLine();  
                System.out.println("INFO:"+result);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }  
    

    此时通过pro.getInputStream(); 获取inputstream,再将流转成字符流,此时便能输出执行shell命令后返回的信息;

    4.2.3 结合业务场景的工具类

    package com.ceb.mental.util;
    
    
    import org.apache.commons.lang.StringUtils;
    
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    
    public class RunShell {
    
    
        private final static String BASH_PATH_PUT = "/home/Axway/Synchrony/SecureClient/sclient script ";
        private final static String BASH_PATH_STATUS = "/home/Axway/Synchrony/SecureClient/sclientadm displayjob -id ";
        private final static String GET = "get";
        private final static String PUT = "put";
    
        /**
         * MFT上传文件
         *
         * @param dirPath  上传文件目录
         * @param fileName 上传文件名称
         * @param dest     上传脚本存放路径
         * @param userId   光大分配id
         * @return
         */
        public static boolean putMFTFile(String dirPath, String fileName, String dest, String userId) {
            return manageMFTFile(dirPath, fileName, dest, userId, PUT);
        }
    
        /**
         * MFT下载文件
         *
         * @param dirPath  下载文件存放的目录
         * @param fileName 下载文件名称
         * @param dest     下载脚本存放路径
         * @param userId   光大分配id
         * @return
         */
        public static boolean getMFTFile(String dirPath, String fileName, String dest, String userId) {
            return manageMFTFile(dirPath, fileName, dest, userId, GET);
        }
    
        public static boolean manageMFTFile(String dirPath, String fileName, String dest, String userId, String manageType) {
            //生成put文件
            String commandFile = VelocityUtil.createMFTCommandFile(manageType, dirPath, fileName, dest, userId);
            //组装put脚本
            String command = BASH_PATH_PUT + commandFile;
            System.out.println("==========>执行脚本:" + command);
            //执行put上传命令
            String result = runCommand(command, 12);
            if (StringUtils.isNotBlank(command)) {
                //截取jobId
                String jobId = result.substring(result.lastIndexOf(":") + 1).trim();
                System.out.println("截取的jobId:" + jobId);
                //查询状态
                String statusCommand = BASH_PATH_STATUS + jobId;
                System.out.println("==========>查询文件状态脚本:" + statusCommand);
                String statusResult = runCommand(statusCommand, 7);
                String status = statusResult.substring(statusResult.lastIndexOf("=") + 1).trim();
                System.out.println("上传结果:" + status);
                if (StringUtils.isNotBlank(status) && status.equals("Finished")) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 运行脚本,并读取特定行信息
         *
         * @param commond  shell脚本
         * @param readLine 从第几行读取
         * @return
         */
        private static String runCommand(String commond, int readLine) {
            String result = null;
            try {
                Process ps = Runtime.getRuntime().exec(commond);
                ps.waitFor();
                BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream()));
                StringBuffer sb = new StringBuffer();
                int iLine = 1;
                String line;
                while ((line = br.readLine()) != null) {
                    if (iLine == readLine) {
                        sb.append(line).append("
    ");
                    }
                    iLine++;
                }
                result = sb.toString();
                System.out.println(result);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    
    }
    

    4.2.4 业务测试

    编写controller.

    package com.ceb.mental.controller;
    
    import com.ceb.mental.util.RunShell;
    import com.ceb.mental.util.VelocityUtil;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/mft")
    public class MFTController {
    
        @RequestMapping("/upload")
        public void putFile(String fileName){
            RunShell.putMFTFile("/home/qy_work/cebfile/upload", fileName,
                    "/home/qy_work/cebfile/upload/put", "USERID");
        }
    
        @RequestMapping("/download")
        public void getFile(String fileName){
            RunShell.getMFTFile("/home/qy_work/cebfile/download", fileName,
                    "/home/qy_work/cebfile/upload/get", "USERID");
        }
    
    }
    
    

    将项目部署到MTF的客户端,访问相应的URL,

    4.2.5 检查是否上传成功

    /home/Axway/Synchrony/SecureClient/sclient cebbank list USERID
    
    • 1

    这里查询的是银行MTF端上传文件的列表。查询结果,如图:

    查询结果

    5. 总结

    刚开始都不知道MFT是啥,然后从搭建客户端,在客户端一点点尝试操作,查阅相关资料,摸清原理,理清业务及其中的技术点,评估技术方案,积跬步、解决bug,最终完成了这个看似很难的需求。

    生命不息、战斗不止!

  • 相关阅读:
    LUA之面向对象
    LUA笔记之表
    LUA笔记之字符串
    STM32模拟I2C
    php(1)-php5.6启动命令
    ip地址变更对tomcat和nginx的影响
    解决 nginx: [alert] kill(189, 1) failed (3: No such process)
    linux(16)-yum安装提示“没有可用软件包”
    性能测试监控分析(17) 负载和CPU使用率低高负载的原因
    Codeforces Round #588 (Div. 2)C(思维,暴力)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13317358.html
Copyright © 2011-2022 走看看