精华内容
下载资源
问答
  • 数据对接
    千次阅读
    2022-01-18 17:20:01

    目录

    基本信息

    网络拓扑

    需要交换机、防火墙确保网络安全;
    前置机原则上需要在网络边界处放置一台(专网可以不需要前置机),可用于大数据量做缓冲,也可用于进行网络安全隔离

    两种模式简介

    1 抽取模式(从业务方拉):

    • 优点:技术实现简单 / 成本低,无需额外费用;
    • 缺点:对源系统容易造成性能问题 / 源系统数据结构发生变化容易出错同步失败 / 源系统对数据质量不负责

    2 供数模式(由业务方推):

    • 优点:对源系统无侵入 / 数据质量可以推给业务端;
    • 缺点:需要额外第三方接口费 / 对接技术实现上相对复杂

    1 抽取模式的三种具体方案

    1.1 抽取模式——WebService接口

    业务系统——接口 <- 接入节点——大数据平台

    • 优点:数据接入时间点和速度可控 / 业务端可控制数据范围和数据加密;
    • 缺点:批量走接口可能导致业务系统不稳定;
    • 适用场景:小批量结构化数据;
    • 不适用场景:大批量非结构化数据 / 实时数据同步

    1.2 抽取模式——直连数据库备库的方式

    业务系统——数据库备库 <- 接入节点——大数据平台

    • 优点:业务系统提供备库,对业务无影响;
    • 缺点:部分场景下业务没有备库 / 数据加密由平台侧保障;
    • 适用场景:小批量结构化数据;
    • 不适用场景:大批量非结构化数据 / 实时数据同步

    1.3 抽取模式——文件同步

    业务系统——文件地址 <- 接入节点——大数据平台

    • 优点:业务系统无感知;
    • 缺点:批量拉对网络波动影响;
    • 适用场景:非结构化数据

    2 供数模式的四种具体方案

    2.1 供数模式——API接口

    业务系统——接口 -> 接入节点——大数据平台

    • 优点:业务系统无感知少风险;
    • 缺点:对平台侧接口性能要求较高 / 需要支付接口开发费用;
    • 适用场景:小批量结构化数据 / 实时数据同步;
    • 不适用场景:大批量非结构化数据

    2.2 供数模式——数据库同步

    业务系统——数据库主库 -> 数据库备库——接入节点——大数据平台

    • 优点:业务系统无感知;
    • 缺点:需要额外接口费;
    • 适用场景:小批量结构化数据 / 实时数据同步;
    • 不适用场景:大批量非结构化数据

    2.3 供数模式——(压缩)文件同步

    业务系统 -> FTP服务器——接入节点——大数据平台

    • 优点:业务系统无感知;
    • 缺点:需要额外接口费;
    • 适用场景:非实时数据同步;
    • 不适用场景:实时数据同步

    2.4 供数模式——实时同步

    业务系统 -> 消息队列(kafka)——接入节点——大数据平台

    • 优点:实时处理数据;
    • 缺点:需要额外接口费;
    • 适用场景:实时数据同步;
    • 不适用场景:非结构化数据
    更多相关内容
  • 全国建筑工人管理服务信息平台数据开放式接口 OpenAPI 主要为第三方服务商与全国建筑工人管理服务信息平台进行数据交互使用;所有 API 均基于 Post方式请求调用。
  • 线上平台与线下某些软件系统要实现数据对接和数据互传,并且不能影响软件系统的正常运行。            其实很多互联网产品中都这样的需求,很多平台运营商吸引线下商家、客户...

    需求场景

    由于产品业务发展,催生出了一个需求:
      线上平台与线下某些软件系统要实现数据对接和数据互传,并且不能影响软件系统的正常运行。
    
      其实很多互联网产品中都这样的需求,很多平台运营商吸引线下商家、客户入驻线上某些平台、
    Sass服务等产品,帮助商家或客户拓展线上业务。在这个前提下,商家或客户实际以在线下开展业务多年,
    有一套完整、成熟、稳健的线下业务流程、线下软件系统(ERP、CRM等)。可以说站在客户的业务角度出
    发,现在业务发展已经演变为线下、线上双箭齐发的场景。由此衍生出数据对接、互通数据的需求。
    
    1.线上平台需要线下基础资料:
        在入驻线上系统初期,很多基础资料来自线下软件系统,例如:线下业务中的客户数据、
        商品数据、库存数据、价格数据等。
    
    2.线下软件需要线上订单数据:
        在完成线下基础资料的数据上传线上平台后,线上平台已经可以正常开展业务,由此产生的线上数据
        例如订单数据需要及时准确的下发传输给线下软件系统。
    

    在这里插入图片描述

    技术分析

    开放API 方式:

    通常线上平台都会伴随系统开发一个开放平台,提供一些基础的数据传输API、业务对接API等。
    这是最常见的也是最容易实施和落地的。这些开放的API接口往往基于Http协议、WebService等,
    常见的大厂向,微信、阿里、饿了么、美团 等等
    这样的方案同时也存在几个致命的缺点:
        1.必须要求客户对接
            谁对接意味着谁来开发对接程序,很多客、商家是不具备开发能力的。
        2.数据下发被动、实时性低
            对线上数据的获取由线下对接程序根据需要调取线上API接口获取。数据的产生
            与数据的下发不同步现象明显,对实时性要求比较高的业务场景如订单,这种方案是不可取的。
    

    免开发方式

    客户是上帝,现在客户说了:什么平台,让我来开发对接。有没有搞错,我是用户,我要是能开发软件,
    还用花钱买你们的软件服务吗!
    
    负责的平台都不应该把本来属于应该自己来解决的问题,推给客户。再者说,如果平台不能提供解决方案,
    来解决这个普遍性的问题,还要什么发展,还能发展吗。
    
    言归正传,免开发对接方案严格意义上,也不是一点开发都不做。它的好处在于不用修改、二开线下软件,
    而是通过部署一个运行在线下的对接程序来完成数据中继。自然,这个对接程序有平台运营商开发并
    提供对接服务。
    
    我们分析一下,线上、线下各运行者一套软件系统,实现两个不想关的系统间数据对接,首先要思考
    两个系统间的数据对接标准是什么,规范是什么。而数据对接的标准和规范应当从业务开展角度出发,
    从实际需求出发。很多线上平台都有成熟的标准,而这些标准都是从一线业务场景中沉淀、总结出来的。
    简单说,平台负责定义数据对接标准,具体说,平台提供商品表、订单表、客户表等数据结构定义、字段
    类型定义、字段长度定义等。
    除此之外,按照数据对接标准开发数据对接客户端程序,来完成线上、线下不同系统间的数据传输工作。
    

    客户端技术特点:

    在这里插入图片描述
    客户端部署在线下,要求能够访问数据对接中间数据库。客户端中运行者若干定时任务,用来按固定频率
    向线上传输数据。同时客户端也监听者线上平台的MQ异步消息,完成线上数据到线下中间库的数据下发。
    这里提到来一个概念:“中间库”。
    所谓中间库,就是部署在线下的数据对接用的数据库,客户端整个运行都围绕着中间数据库开展业务。
    上传数据:
    1.在中间库中根据业务编写不同的存储过程,来访问线下软件的业务库数据
    2.数据对接中间程序运行着定时任务,负责调取存储过程中的线下数据,通过MQ、API等方式
    抽送到线上平台
    下发数据:
    1.线上平台产生业务数据,将数据存放到MQ服务中的指定消息队列
    2.数据对接客户端监听数据下发消息队列,获取消息中的数据后,写入到中间库、或者调用存储过程完成
    数据写入到线下业务数据库中

     由此,可得出结论。数据对接客户端,对接数据依赖数据库编程(存储过程),消息服务MQ以及接口API。
     整个流程并不需要对客户的线下软件系统进行改造和二开。
    

    免开发实时性方案思考

        综合开放API方案、数据对接客户端方案不难发现,都无法完美的实现数据对接绝对实时。
        于是笔者认为,要解决线下数据上传过程的数据监听问题,可能有些读者已经考虑到数据的触发器了。
        对于数据库的数据变动触发器能够及时的捕捉到,并且提供了数据编程的可能。那么剩下来的问题
        就是如何实现将触发器捕获的数据变动通知给数据对接客户端。如果能够实现此一步,那么对于线下
        软件产生的业务数据就能够在第一时间上传给线上平台了。
        通过与负责实施的同时沟通了解到,可以在触发器中向数据对接客户端发起Http 调用,将数据传送给
        数据对接客户端。目前向Oracle、SQLServer、Mysql 都可以做到。
        到此可以得出结论,只要业务需要,无论是对数据的上传还是下发,线上、线下两个系统间都可以通过
        技术手段实现数据实时传递。
        遗憾的是我们业务上允许上行数据的小幅度延迟,没有得到实际的将想法思路通过实际进行检验。
        我想理论方案可行,将我的想法思路分享给各位奋斗在一线开发的小伙伴们!
    

    在这里插入图片描述

    程序监听数据库变化

    关于程序中如何监听数据库变化,我的另一篇博客
    https://blog.csdn.net/yanghui555/article/details/115356763程序监听数据库变化的方法和实践,Oracle、Sqlserver、Mysql

    展开全文
  • 浅谈数据对接

    千次阅读 2020-03-16 15:47:19
    浅谈数据对接 一般来讲,数据对接就是将己方收集到的客户数据转化为平台方的规范要求的数据格式上传的一种方式。各个平台对于数据的要求格式各不相同。那怎么才能快速做好数据对接,而且重复劳动尽量少地完成,返工...

    浅谈数据对接

    	一般来讲,数据对接就是将己方收集到的客户数据转化为平台方的规范要求的数据格式上传的一种方式。各个平台对于数据的要求格式各不相同。那怎么才能快速做好数据对接,而且重复劳动尽量少地完成,返工也要尽量少。我觉得要分几个步骤去进行。切记不能一头热栽在文档上,死命地对,很可能你的接口后期要崩死,考虑平台方的修改和己方数据的缺失或者并不完全符合的情况。
    1.粗略看下对接文档和客户需求(这个必须看,不看后期等着改死吧)。特别对日期敏感,数据对接很可能为了验收  ,有一段日期里的数据特别重要。
    2.了解收集到的客户数据,并将字段和了解到的对接文档字段一一对应。对于编码一定要重视,一般数据上传失败会在这里栽跟头。
    3.等了解了己方数据库字段和文档的对应关系后,别急着下手。还有一步是一定要做的。查看数据库的垃圾数据,想必大家都知道,但凡数据大了之后,总有垃圾数据会在上传的时候影响上传的数据的准确性。当然会有人建立日志去记录,上传数据的成功率。但是去搜索日志需要耗费精力,还要去确定问题的根源,会浪费大量时间。与其去后期处理,不如预先做好预防,提前将垃圾数据给筛掉,保证数据上传的精确。
    4.前面工作做完才能安心码代码编写接口将数据上传。
    

    下面举个例子 (后台框架mybatis+mvc)
    对接方式 :导出txt (昨天的数据) 再上传平台
    后台sql

        <select id="getMeatTzInHz" resultType="net.***.**.platform.meat.entity.MeatTzInHz">
    		SELECT
    			instock.slaughterCode,
    			instock.instockDate,
    			SUM(instock.checkInstockNum),
    			AVG(instock.price) 				AS price,
    			SUM(instock.quantityAndWeight)  AS quantityAndWeight,
    			SUM(instock.transportDeathNum)  AS transportDeathNum,
    			produceCode
    		FROM (
                     SELECT
                        COALESCE(trace_mainbody_employee.mainbody_record_info :: JSON ->> 'meatCommerceCode', '')  AS slaughterCode,
                        --主体编码
                        COALESCE(trace_mainbody_employee.mainbody_name, '')                                    AS slaughterName,
                        --主体名称
                        TO_CHAR(trace_instock.instock_date,'yyyyMMdd')                  		    AS instockDate,
                        --进场日期
                        COALESCE(substr(trace_mainbody_provider.mainbody_record_info :: JSON ->> 'commerceCode',1,9), '') AS ownerCode,
                        --货主编码
                        COALESCE(trace_mainbody_provider.mainbody_name, '')                               		 AS ownerName,
                        --货主名称
                        COALESCE(trace_batch.inspection_info :: JSON ->> 'CitypfTzPigCertCode', '')       		 AS checkCertCode,
                        --生猪产地检疫证号,--json格式要得到CitypfTzPigCertCode
                        CAST(COALESCE(trace_batch.batch_other_info :: JSON ->> 'detectionQuantity', '0') AS int) AS checkInstockNum,
                        --检疫证进场数量,--json格式要得到CitypfTzQuarantineCount
                        COALESCE(trace_batch.batch_price, 0)                                              		 AS price,
                        --批次单价
                        COALESCE(trace_batch.batch_quantity, 0)                                           		 AS quantityAndWeight
    
                        FROM
                            t_trace_instock trace_instock --入库基础
                            INNER JOIN t_trace_instock_detail trace_instock_detail
                        ON (
                            trace_instock.ID = trace_instock_detail.instock_id
                            AND trace_instock_detail.del_flg = '0'
                            )
                            INNER JOIN t_trace_mainbody trace_mainbody_employee --企业表
                            ON (
                            trace_mainbody_employee.trace_mainbody_code = trace_instock.trace_mainbody_code
                            AND trace_mainbody_employee.mainbody_type in ('101')
                            AND trace_mainbody_employee.del_flg = '0'
                            AND trace_mainbody_employee.mainbody_record_info :: JSON ->> 'meatCommerceCode' != ''
    						<if test="division != null and division != ''">
    							  AND split_part(trace_mainbody_employee.mainbody_record_info :: JSON ->> 'divisionName',',',2) like ('%' || #{division,jdbcType = VARCHAR} || '%')
    						</if>
    						<if test="mainBodyCode != null and mainBodyCode != ''">
    							AND main.mainbody_code = #{mainBodyCode,jdbcType = VARCHAR}
    						</if>
                            )
                            LEFT JOIN t_trace_mainbody trace_mainbody_provider --供应商表
                            ON (
                            trace_mainbody_provider.mainbody_code = trace_instock.supplier_code
                            AND trace_mainbody_provider.mainbody_type = '102'
                            AND trace_mainbody_provider.del_flg = '0'
                            )
                            INNER JOIN t_trace_batch trace_batch --批次表
                            ON (
                            trace_batch.batch_code = trace_instock_detail.batch_code
                            AND trace_batch.del_flg = '0'
                            )
                        WHERE
                            trace_mainbody_employee.mainbody_record_info :: JSON ->> 'nodeType' = '101'
                            AND trace_instock.del_flg = '0'
                          <![CDATA[
                            AND CAST(trace_instock.sys_reg_tmsp AS DATE) >= CAST(#{startTagSysRegTmsp,jdbcType = VARCHAR} AS DATE)
                            AND CAST(trace_instock.sys_reg_tmsp AS DATE) <= CAST(#{endTagSysRegTmsp,jdbcType = VARCHAR} AS DATE)
                          ]]>
    		) instock
    		GROUP BY instock.slaughterCode, instock.instockDate, produceCode
    	</select>
    
    

    实体

    public class MeatTzInHz implements Serializable {
    
        /**
         * 屠宰厂编码
         */
        protected String slaughterCode;
    
        /**
         * 进厂日期
         */
        protected String instockDate;
    
        /**
         * 检疫证进场数量
         */
        protected int checkInstockNum;
    
        /**
         * 采购价
         */
        protected double price;
    
        /**
         * 实际数量和重量
         */
        protected double quantityAndWeight;
    
        /**
         * 途亡数
         */
        protected int transportDeathNum;
    
        /**
         * 产地编码
         */
        protected String produceCode;
    
        @Override
        public String toString() {
            return  FileEnum.DIVIDE_COMMA.getSymbol() + slaughterCode +
                    FileEnum.DIVIDE_COMMA.getSymbol() + instockDate +
                    FileEnum.DIVIDE_COMMA.getSymbol() + checkInstockNum +
                    FileEnum.DIVIDE_COMMA.getSymbol() + price +
                    FileEnum.DIVIDE_COMMA.getSymbol() + quantityAndWeight +
                    FileEnum.DIVIDE_COMMA.getSymbol() + transportDeathNum +
                    FileEnum.DIVIDE_COMMA.getSymbol() + produceCode + FileEnum.ENTER_LINE_BREAK.getSymbol();
        }
    

    mapper

    List<MeatTzOutHz> getMeatTzOutHz(DataCondition dataCondition);
    

    service

        public List<MeatTzInHz> getMeatTzInHz(DataCondition dataCondition) {
            List<MeatTzInHz> meatTzInHzList = meatMapper.getMeatTzInHz(dataCondition);
            //验证数据非空
            if(CollectionUtils.isEmpty(meatTzInHzList)) {
                return new ArrayList<>();
            }
            return meatTzInHzList;
        }
    

    导出service(方法名自写,只剖了方法体) ExportService

            List<MeatTzOutHz> meatTzOutHzList = meatService.getMeatTzOutHz(dataCondition);
            meatTzOutHzList.stream()
                    .filter(meatTzOutHz -> StringUtils.isNotBlank(meatTzOutHz.getSlaughterCode()))
                    .forEach(meatTzOutHz -> {
                        String fileName = txtFileUtil.getFileName(FileEnum.POSTFIX_MEAT_TZ_OUT_HZ_TXT.getSymbol(), meatTzOutHz.getSlaughterCode(), meatTzOutHz.getOutstockDate());
                        try {
                            txtFileUtil.writeFileToDateDirectory(meatTzOutHz.toString(), fileName);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    });
    

    定时任务
    大同小异

        @Autowired
        private ExportService exportService;
    /**
    *  秒  分   时  日  月  年  
    */
        @Scheduled(cron = "0 30 4,11 * * ? ")
        private void grouppurchasing() {
            List<MainBody> mainBodyList = nodeService.getMainBodyByNodeType(2);
    
            if(CollectionUtils.isNotEmpty(mainBodyList)){
                logger.info("生成数据文件开始!");
                mainBodyList.forEach(mainBody -> {
                    logger.info("生成企业:{} 数据文件开始!", mainBody.getMainbodyName());
                    DataCondition dataCondition = new DataCondition();
                    dataCondition.setDivision("上海");
                    dataCondition.setStartTagSysRegTmsp(DateUtil.getYesterdayByDefultFormat());
                    dataCondition.setEndTagSysRegTmsp(DateUtil.getNowByDefultFormat());
                    //dataCondition.setNodeTypeList(Arrays.asList(PlatformConstant.NODE_TYPE_110));
                    dataCondition.setMainBodyCode(mainBody.getMainbodyCode());
    
                    baseNodeInfo(dataCondition ,true);
    
                    exportService.exportGpToTxt(dataCondition);
                    logger.info("生成企业:{} 数据文件结束!", mainBody.getMainbodyName());
                });
                logger.info("生成数据文件结束!");
            }
    
        }
    

    工具类 fileEnum txtFileUtil

    public enum FileEnum {
    POSTFIX_MEAT_TZ_OUT_TXT("{0}_{1}_T2.TXT"),;
        private String symbol;
    
        FileEnum(String symbol) {
            this.symbol = symbol;
        }
    
        public String getSymbol() {
            return symbol;
        }
    }
    
        public String getFileName(String pattern, Object... arguments) {
            if (arguments == null || arguments.length == 0) {
                return pattern;
            }
            return MessageFormat.format(pattern, arguments);
        }
        /**
         * 写文件
         *
         * @param line
         * @param fileName
         * @throws IOException
         */
        public void writeFileToDateDirectory(String line, String fileName) throws IOException {
            String dateDir = DateUtil.getYesterdayByDefultFormat() + "/";
            String dirFile = this.fileOutputDirectory + dateDir + fileName;
            byte[] buff = line.getBytes(Charset.forName("GBK"));
    
            FileOutputStream out = fileMap.get(dirFile);
            if (out == null) {
                File directoryFile = new File(this.fileOutputDirectory + dateDir);
                File file = new File(dirFile);
                if (!directoryFile.exists()) {
                    directoryFile.mkdirs();
                }
                // 文件不存在,创建
                if (!file.exists()) {
                    file.createNewFile();
                }
                // append 参数为false,覆盖原来的文件
                out = new FileOutputStream(file, false);
                fileMap.put(dirFile, out);
            }
            out.write(buff);
        }
    

    DateUtil

        private static Date add(Date date, int calendarField, int amount) {
            if (date == null) {
                throw new IllegalArgumentException("The date must not be null");
            } else {
                Calendar c = Calendar.getInstance();
                c.setTime(date);
                c.add(calendarField, amount);
                return c.getTime();
            }
        }
        /**
         * 增加天
         *
         * @param date
         * @param amount
         * @return
         */
        public static Date addDays(Date date, int amount) {
            return add(date, 5, amount);
        }
            /**
         * 获取昨天
         * @return
         */
        public static Date getYesterday() {
            return addDays(getNow(), -1);
        }
            /**
         * 把日期类型格式化成字符串
         *
         * @param date
         * @param format
         * @return
         */
        public static String convert2String(Date date, String format) {
            SimpleDateFormat formater = new SimpleDateFormat(format);
            try {
                return formater.format(date);
            } catch (Exception e) {
                return null;
            }
        }
            /**
         * 获取昨天的日期并转化为yyyyMMdd
         * @return
         */
        public static String getYesterdayByDefultFormat() {
            return convert2String(getYesterday(), DATE_FORMAT_YYYYMMDD);
        }
    

    到这里,应该能生成txt文件了。还有一步上传。
    平台方要求 生成zip 再用webservice的方式上传到平台
    先po功能代码 ,再给工具类

        /**
         * webservice  上传zip文件(一天一次)
         * 6:30 12:30
         */
        @Scheduled(cron = "0 30 6,12 * * ? ")
        private void uploadZipByWebservice() {
            //打包
            String zipPath = "/sh";
            String zipName = DateUtil.getYesterdayByDefultFormat();
            ZipUtils.createZipFile(zipPath,zipName);
            //地址
            String fileName = "sh" + zipName + ".zip";
            Identity identity  = new Identity();
            ReqEntity<Identity> reqEntity = new ReqEntity<>();
            identity.setFileName(fileName);
            identity.setFileCreateTime(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            File file = new  File("/data/"+ fileName);
            identity.setFileSize(String.valueOf(file.length()));
            reqEntity.setIdentity(identity);
            reqUtil.sendXml(reqEntity);
        }
    

    看着很简洁 ,包含了三个工具类,和 两个实体类
    实体类

    @Data
    //只检测属性不检测方法了
    @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE)
    public class Identity implements Serializable {
    
        private String UserName;
        private String Password;
        private String FileName;
        private String FileSize;
        private String FileCreateTime;
    }
    
    

    父类

    @Data
    //只检测属性不检测方法了
    @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE)
    public class ReqEntity<Identity> implements Serializable {
    
        private String pfileType;
        private String pbuffer;
        private String usercode;
        private String userPass;
        private Identity identity;
        private  String pErrmasg;
    
    }
    

    其实就是定义一些字段,啥也没写。=。=
    工具类的话
    生成压缩文件 zipUtil

        /**
         * 创建压缩文件
         */
        public static void createZipFile(String saveFilePath ,String zipName) {
            FileOutputStream fileOutputStream = null;
            try {
                File zipFile = new File(saveFilePath + zipName.replaceAll("(.*?)(/*)$","$1") + ".zip");
                fileOutputStream = new FileOutputStream(zipFile);
                ZipUtils.toZip(saveFilePath + "/"+zipName+"/", fileOutputStream, false);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        /**
         * 压缩成ZIP 方法1
         *
         * @param srcDir           压缩文件夹路径
         * @param out              压缩文件输出流
         * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
         *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
         * @throws RuntimeException 压缩失败会抛出运行时异常
         */
        public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure)
                throws RuntimeException {
    
            long start = System.currentTimeMillis();
            ZipOutputStream zos = null;
            try {
                zos = new ZipOutputStream(out);
                File sourceFile = new File(srcDir);
                compress(sourceFile, zos, sourceFile.getName(), KeepDirStructure);
                long end = System.currentTimeMillis();
                logger.info("压缩完成,耗时:" + (end - start) + " ms");
            } catch (Exception e) {
                throw new RuntimeException("zip error from ZipUtils", e);
            } finally {
                if (zos != null) {
                    try {
                        zos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        /**
         * 递归压缩方法
         *
         * @param sourceFile       源文件
         * @param zos              zip输出流
         * @param name             压缩后的名称
         * @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;
         *                         false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
         * @throws Exception
         */
        private static void compress(File sourceFile, ZipOutputStream zos, String name,
                                     boolean KeepDirStructure) throws Exception {
            byte[] buf = new byte[BUFFER_SIZE];
            if (sourceFile.isFile()) {
                // 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
                zos.putNextEntry(new ZipEntry(name));
                // copy文件到zip输出流中
                int len;
                FileInputStream in = new FileInputStream(sourceFile);
                while ((len = in.read(buf)) != -1) {
                    zos.write(buf, 0, len);
                }
                // Complete the entry
                zos.closeEntry();
                in.close();
            } else {
                File[] listFiles = sourceFile.listFiles();
                if (listFiles == null || listFiles.length == 0) {
                    // 需要保留原来的文件结构时,需要对空文件夹进行处理
                    if (KeepDirStructure) {
                        // 空文件夹的处理
                        zos.putNextEntry(new ZipEntry(name + "/"));
                        // 没有文件,不需要文件的copy
                        zos.closeEntry();
                    }
                } else {
                    for (File file : listFiles) {
                        // 判断是否需要保留原来的文件结构
                        if (KeepDirStructure) {
                            // 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
                            // 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
                            compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
                        } else {
                            compress(file, zos, file.getName(), KeepDirStructure);
                        }
                    }
                }
            }
        }
        /**
         * 根据文件路径读取文件转出 byte[]
         *
         * @param filePath 文件路径
         * @return 字节数组
         * @throws IOException
         */
        public static byte[] getFileToByteArray(String filePath) throws IOException {
            File file = new File(filePath);
            long fileSize = file.length();
            if (fileSize > Integer.MAX_VALUE) {
                return null;
            }
            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] buffer = new byte[(int) fileSize];
            int offset = 0;
            int numRead = 0;
            while (offset < buffer.length && (numRead = fileInputStream.read(buffer, offset, buffer.length - offset)) >= 0) {
                offset += numRead;
            }
            // 确保所有数据均被读取
            if (offset != buffer.length) {
                logger.error("Could not completely read file : {}" , file.getName());
                throw new IOException("Could not completely read file " + file.getName());
            }
            fileInputStream.close();
            return buffer;
        }
        
    

    还有上传的一个工具类 reqUtil

        @Value("${webservice.url}")
        private String webUrl;
        /**
         * xml  上报方式
         * @param reqEntity
         * @return
         */
        public String sendXml( ReqEntity<Identity> reqEntity) {
            URL url = null;
            String fileName = reqEntity.getIdentity().getFileName();
            String fileSize = reqEntity.getIdentity().getFileSize();
            String createTime = reqEntity.getIdentity().getFileCreateTime();
            String fileStr = "";
            String responsMess  = "";
            try {
                url = new URL(webUrl);
                reqUtilLogger.info("requestUrl:" + url);
                HttpURLConnection con = (HttpURLConnection) url.openConnection();
                byte[] zipOutput = ZipUtils.getFileToByteArray("/data/"+ fileName);
                fileStr = new sun.misc.BASE64Encoder().encode(zipOutput);
    
                //拼接好xml
                StringBuffer sb = new StringBuffer();
                sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
                        "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" +
                        "  <soap:Header>\n" +
                        "    <Identify xmlns=\"FjUpdate1\">\n" +
                        "      <UserName>"+userName+"</UserName>\n" +
                        "      <Password>"+password+"</Password>\n" +
                        "      <FileName>"+fileName+"</FileName>\n" +
                        "      <FileSize>"+fileSize+"</FileSize>\n" +
                        "      <FileCreateTime>"+createTime+"</FileCreateTime>\n" +
                        "    </Identify>\n" +
                        "  </soap:Header>\n" +
                        "  <soap:Body>\n" +
                        "    <UpFile xmlns=\"FjUpdate1\">\n" +
                        "      <pFileType>" + fileName.split("\\.")[0] + "</pFileType>\n" +
                        "      <pBuffer>"+fileStr+"</pBuffer>\n" +
                        "      <usercode>"+userName+"</usercode>\n" +
                        "      <userpass>"+password+"</userpass>\n" +
                        "    </UpFile>\n" +
                        "  </soap:Body>\n" +
                        "</soap:Envelope>\n");
    
                String xmlStr = sb.toString();
                System.out.println(xmlStr);
                //设置好header信息
                con.setRequestMethod("POST");
                con.setRequestProperty("content-type", "text/xml; charset=utf-8");
                con.setRequestProperty("Content-Length", String.valueOf(xmlStr.getBytes().length));
    //      con.setRequestProperty("soapActionString", soapActionString);
    
                //post请求需要设置
                con.setDoOutput(true);
                con.setDoInput(true);
    
                //对请求body 往里写xml 设置请求参数
                OutputStream ops = con.getOutputStream();
                ops.write(xmlStr.getBytes());
                ops.flush();
                ops.close();
    
                //设置响应回来的信息
                InputStream ips = con.getInputStream();
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buf = new byte[1024];
                int length = 0;
                while( (length = ips.read(buf)) != -1){
                    baos.write(buf, 0, length);
                    baos.flush();
                }
                byte[] responsData = baos.toByteArray();
                baos.close();
    
                //处理写响应信息
                responsMess = new String(responsData,"utf-8");
                reqUtilLogger.info("响应报文:"+ responsMess);
                reqUtilLogger.info(String.valueOf(con.getResponseCode()));
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return responsMess;
        }
    

    这里@value的参数就不po了,大家懂的自然懂,怎么配置。
    userName和password自行定义即可。

    这样,就大功告成了。(^-^)V。
    很多人会问,用这么多类,你的 maven需要导入很多依赖包
    基础的mybatis,springframework等的我就不po了,大家都知道的。然后就是正常的io依赖也没啥特别的。自行解决就好。

    展开全文
  • 在做涉及到多家部门或需要多家合作的项目时,往往会涉及到数据对接,这里简单介绍下一些数据对接会议召开和内容项包括一些经验。 一、会前准备 数据对接函(文) 在进行对接前,让项目的业主给需要对接数据的对接...

    在做涉及到多家部门或需要多家合作的项目时,往往会涉及到数据对接,这里简单介绍下一些数据对接会议召开和内容项包括一些经验。

    一、会前准备

    数据对接函(文)

    在进行对接前,让项目的业主给需要对接数据的对接方,以公函(文)或邮件方式发送一个书面的对接说明。

    函上主要可写三块内容:1.背景介绍 2.对接需求 3.要求对方指派、回复相关对接联络人联系方式

    简单介绍下背景(项目建设背景),主要是要用“帽子来压一压”,然后在具体对接内容上如果业主建设需求相当明确,就可以以附件形式将需要对接的内容项一并发送。

    请配合进行数据对接,相关对接内容详见附件。

    若建设内容比较模糊化,对接内容上就写的模糊些,例如

    请配合在 业务数据、库表结构和相关统计类数据进行对接。

    备好清单

     在去进行数据对接会议之前罗列好所需数据清单,最好可打印出来(2份),若是不了解对方业务情况,可以先准备一个通稿,以免对方用“你想要什么数据,我们就提供什么数据”来堵你,有了一个通稿在会议上也可强调可根据对方业务实际情况进行调整。

    联络时间+地点

    有了联系人,就要提前约好联络时间以及会议进行地点。

    二、进行会议

    进行的会议最好有业主方陪同,一般在前期的沟通会上,也是对方的业主在场,而且业主方出席会议的话,在对接推进上力度会大增,对方的配合意愿也会大大增强。

    整个会议中,作为技术公司可以主要沟通5项内容:

    1. 对接内容(需求):把自身需要的数据需求给对方明确
    2. 对接联络人:在会议上再次明确相关负责对接事项的联系人,最好固定到一个人,多人容易推诿扯皮
    3. 对接方式:目前在做的主要是ToG项目,对方的一些平台都是省部级平台,非自建的何难对接。此时就要两条腿走路,技术对接为主,表格为辅助。
    4. 对接时间:这一点也是重中之重,在会议上沟通好对接时间点,明确到开始日期,方便后期的对接工作推进
    5. 对接费用:这一点不是必须的,但也可能涉及到,如果对方未在会议提及,己方不用主动提

    ps:如果你不清楚对方的业务,可以先查些资料,然后在会议上调研了解下对方的业务情况。如果整个沟通比较顺畅,最好的情况下,可以请对方业主带己方参观下他们已建的系统,以便自己更有针对性地对数据对接内容进行优化调整。

    三、会后沟通

    所谓趁热打铁才能事半功倍,在会后与对方数据对接负责人联系上后,最好第一时间就把相关资料发送给对方,并可再强调下时间

    “您看您这边能否尽快先给到我们一些数据,让我们进行测试”“为推进任务,请于XX前进行推送”....

     

    展开全文
  • JAVA实现两个系统数据对接

    千次阅读 2021-04-20 18:35:56
    前言 最近做了两个系统,现在有一个需求,需要做数据对接,这个有很多方法实现,我这里用的是接口对接。由于是第一次做这种功能,也踩了不少坑,所以在这里记录一下。 提示:以下是本篇文章正文内容,下面案例仅供参考 ...
  • PDM 和 ERP 数据对接面临的问题点 PDM 和 ERP 是在企业信息化过程中发挥重要作用的系统随着它们的发展 两者出现了功能上的交叉当同时维护相同部分数据时就可能出现数据不一致的 情况而且信息无法在两个系统之间相互...
  • 通过在数据处理流程中加入数据对接处理算法,解决了发散收敛波问题,得到一种改进的雨流法实时计数模型.其计数简单,计数时不需要得到完整的应力时间历程,也不需要在计数前对应力时间历程作任何调整或修正,实现了应力...
  • 一、填报账户开通流程 二、数据对接工作流程 一、各部门工作内容如下: 二、对接过程中遇到的问题及对策: 三、针对在全国三甲医院范围内推广该项工作的建议:
  • 人脸识别系统在智慧校园应用中数据对接模型设计.pdf
  • PZEM-004T-100A 交流通讯盒数据对接 JAVA 使用maven 主要看启动类,完全可以使用
  • 生产环境地址为:10.3.6.23:11883用户名/密码:iccp/ .iCCP@2020_emqx上行数据及主题名称BSM数据TOPIC:V2X.RSU.B
  • 数据对接 - 大屏云极简使用手册

    千次阅读 2022-03-18 13:39:01
    大屏云(www.dapingyun.com)是一款制作可视化数据大屏的在线搭建工具服务,组件丰富,开箱即... 本文以快速精简的方式对大屏云数据对接的使用进行说明。 1、数据的接入类型 我们将数据分为如下三种类型,这三种数据的
  • 1.1 对接价值 1.2 应用场景 1.3 对接流程 1.4 对接原理 1.5 基本名词介绍 2.1 步骤 1: 推啊 a_old 获取 2.2 步骤 2: 推
  • 水利普查成果与防洪工程数据库数据对接研究
  • 移动广告平台力美多盟点入 iOS广告主APP敷活数据对接 上海泽思网络科技有限公司 更新时间:2013-10-15 TOC \o "1-5" \h \z \o "Current Document" 一 力美APP激活数据对接 3 \o "Current Document" 目的 3 \o ...
  • 住院病案首页数据采集接口标准
  • 数据对接方式

    千次阅读 2018-12-19 16:35:00
    1 主动推送给对方 需要索取对方的标识id, 公钥,加密算法,私钥等 2 主动从对方那里访问获取 需要索取对方的标识id, 公钥,加密算法,私钥等 3 对方来我们这里访问获取 需要提供本方 标识id, 公钥,加密...
  • 数据对接没有实时在数据管理云平台实时填报.ppt
  • HQMS数据对接文档

    2013-04-25 18:55:31
    HQMS数据对接,卫生部数据接口,用于医院连接卫生部接口
  • 虚拟现实与CAD模型的数据对接探讨.pdf
  • 病案首页数据对接标准
  • 关于aep平台数据与thingsBoard平台对接一、aep与thingsBoard连接方法 (1)新建项目,构造本地服务localhost:8080/receive (2)内网穿透:localhost:8080/receive ----> https://5k5047115i.zicp.fun/ 3.配置数据...
  • HQMS医院质量管理监测系统数据对接接口标准
  • StreamSet作为大数据ETL工具的选择,当然需要处理数据的合并与转存,其大部分功能集中于此。有些场景可能需要其对外提供个接口,可以被调用,StreamSets能做到吗?答案是能!并且能做当今流行的微服务,啊!好高大上...
  • 福建省“一品一码”食品安全追溯管理食品生产经营企业数据对接接口技术规范标准.doc
  • 应用系统数据对接几种方案

    万次阅读 2018-03-12 09:21:59
    应用系统之间数据传输的几种方式第一种方案:socket方式 Socket方式是最简单的交互方式。是典型才C/S交互模式。一台客户机,一台服务器。服务器提供服务,通过IP地址和端口进行服务访问。而客户机通过连接服务器...
  • 对接真实数据 从0开发前后端分离企业级上线项目 课程介绍与开发前的准备工作、开发环境搭建与开发工具使用技巧、项目初始化与基于模块化的脚手架搭建、项目通用功能开发、用户模块开发、商品模块开发、购物车模块...
  • 电信云IOT平台开发详解.rar,电信云IOT平台开发详解,平台创建文档资料,中国电信物联网开放平台_NB-IoT模组对接指导书_v1.2.pdf,中国电信物联网开放平台_NB-IoT业务对接指导书_v1.2.pdf,NB-iot测试工具.rar,Profile和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 179,706
精华内容 71,882
关键字:

数据对接