为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

[要略]巧用jsp完成清双数据压缩下载

2017-09-16 8页 doc 24KB 3阅读

用户头像

is_496339

暂无简介

举报
[要略]巧用jsp完成清双数据压缩下载[要略]巧用jsp完成清双数据压缩下载 巧用JSP实现清单数据压缩下载 申卫兵 , 问题引入 在实际项目开发过程中,我们经常会遇到用户需要在浏览器端下载格式清单数据的问题。一般小数据量(小于1M)采用JSP或Servlet生成txt、CVS、XLS等格式文件就可轻松完成,但是用户在一次下载事件中只能下载一个文件。笔者曾碰到这样的用户需求:用户需要按照登陆用户的地域权限进行下载,权限大的可能下载多个清单文件,比如省公司用户下载多个本地网的清单数据,本地网用户下载多个营业区的清单数据,而且下载的数据量可能超过50M,这种情况下...
[要略]巧用jsp完成清双数据压缩下载
[要略]巧用jsp完成清双数据压缩下载 巧用JSP实现清单数据压缩下载 申卫兵 , 问引入 在实际项目开发过程中,我们经常会遇到用户需要在浏览器端下载格式清单数据的问题。一般小数据量(小于1M)采用JSP或Servlet生成txt、CVS、XLS等格式文件就可轻松完成,但是用户在一次下载事件中只能下载一个文件。笔者曾碰到这样的用户需求:用户需要按照登陆用户的地域权限进行下载,权限大的可能下载多个清单文件,比如省公司用户下载多个本地网的清单数据,本地网用户下载多个营业区的清单数据,而且下载的数据量可能超过50M,这种情况下,常见的普通方法很难处理。本文主要讲述该需求的技术实现方法。 , 问题分析 众所周知,Java JDK对数据ZIP格式的压缩支持是比较好的,如果应用Apache的commons-compress-1.0.jar包,支持的压缩格式会更多;如果采用ZIP压缩,可以使多个文件被压缩成一个压缩文件,压缩文件占用的磁盘空间较少并且可以被快速地进行网络传输。因为我们涉及的清单数据有近50M,如果每次都先将它们从数据库查出来再压缩,Java线程消耗的内存将会很大,这种实现方法不太可行。 我们可以设想一下:如果可以一边从数据库读取数据记录,一边进行压缩,同时进行网络传输下载,这样一来Java线程占用的内存资源也不会很多,而且下载效率很高。 要实现这种想法,我们得解决以下几个关键环节:第一,从数据库读取数据是一点点进行的,而不是一次读取到Java对象列表中;第二,压缩必须是以字节流的方式进行,并且还不能产生中间临时文件;第三,JSP所编译的Servlet在生成部分压缩数据时就开始进行传输,而不是等数据全部压缩完毕再进行传输。 下面我们着重解决这三个问题。 , 问题解决 下载代码可以在JSP中进行编写。因为JSP调整比较方便,并且可以进行热部署而不用重启Web Server,另外通过JSP获取Spring上下文比较容易,再通过Spring上下文可以轻松取得DataSource。而如果选择Servlet进行开发,则要在web.xml文件中进行配置,比较烦琐。 从数据库中获取数据的方法选择JDBC方式,而不要使用JdbcTemplate、IBATIS等ORM(Object Relational Mapping对象关系映射)持久层框架,因为使用这些框架查询数据时都是一次性将全部记录查出,并映射生成Java 对象列表,使用ORM框架读取记录方式会过多地占用JVM内存 ;而JDBC中的 ResultSet对象是以游标方式处理数据的,从ResultSet对象循环读取数据,采用这种方式,数据是一点点的从数据库中被取出的。 JDK ZIP包的ZipOutputStream类能对写入的字节流数据进行压缩,然后到 CheckedOutputStream输出流,CheckedOutputStream对数据进行统计校验,然后数据需要到另外一个输出流中,如果这个输出流是文件,那么就生成ZIP文件了,ZipOutputStream通过写入另外一个ZipEntry对象能实现压缩多个文件在一个压缩包中。 JSP中有个隐含对象response,通过response.getOutputStream()可以得到一个OutputStream输出流对象,通过向这个输出流写入字节流数据,当数据量超过response对象缓冲区8K大小时,这个缓冲区的数据会被从Server端推向浏览器端,直到JSP生成的Servlet对象的对应方法执行完毕。Response输出流缓冲区的大小可以通过response.getBufferSize()得到,也可以通过response.setBufferSize()方法进行设置,默认是8092字节。 在开发JSP下载的过程中,下载文件和ZIP里面的中文文件名会乱码。解决下载文件名乱码的问题,只要把JSP文件按GBK方式创建,另外需对下载文件名称进行utf-8转码;解决下载 ZIP文件中中文文件名乱码的问题,需要把JDK源代码 java.util.zip 包下的ZipOutputStream.java文件中getUTF8Bytes(String s)方法里面代码注释,直接使用“return s.getBytes("gbk")“返回GBK编码,因为rar等解压缩文件识别不了Unicode编码的文件名。也可以在ZipOutputStream类里面重载ZipOutputStream构造方法,把编码方式传入,进行更细致的调整。 到此,我们只要将CheckedOutputStream对象需要的输出流指向response对象的输出流即可。限于篇幅问题,我精简了一个示例: <%@page import="java.sql.*,utils.*,java.io.*" %> <%@page import="java.util.zip.Adler32"%><%@page import="java.util.zip.CheckedOutputStream" %> <%@ page import="javax.sql.DataSource"%> <%@page import="java.net.URLEncoder"%> <%@page language="java" pageEncoding="GBK"%> <%@ page import="org.springframework.context.ApplicationContext"%> <%@ page import="org.springframework.web.context.support.WebApplicationContextUtils"%> <% String splitChar="|"; String lineEndChar="\n"; String filedisplay="转型业务家庭障碍清单.zip"; String month_id="200911"; //下载的压缩文件名称 filedisplay = URLEncoder.encode(filedisplay, "utf-8"); response.addHeader("Content-Disposition", "attachment;filename=" + filedisplay); OutputStream osPage = null; Connection conn = null; Statement stmt = null; ResultSet rs = null; try { ApplicationContext ctx = WebApplicationContextUtils .getWebApplicationContext(request.getSession().getServletContext()); //获取Spring数据源 DataSource ds = (DataSource) ctx.getBean("dataSource"); conn = ds.getConnection(); osPage = response.getOutputStream(); stmt=conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); String sql="select LATN_ID,LATN_NAME,OFR_DEV_NUM "+ " from DM_TCFX_BRAND_DEV_BILL" + " WHERE MONTH_ID =" + month_id + " order by LATN_ID" ; System.out.println(sql); rs=stmt.executeQuery(sql); CheckedOutputStream cout = new CheckedOutputStream(osPage, new Adler32()); ZipOutputStream zout = new ZipOutputStream( new BufferedOutputStream(cout)); String curLatnCode=""; int count=0; if(rs!=null) while(rs.next()) { //根据不同的本地网生成多个文件 if(!rs.getString("LATN_ID").toString(). equalsIgnoreCase(curLatnCode)){ filedisplay = "清单_" + rs.getString("LATN_NAME") + "_" + month_id + ".csv"; zout.putNextEntry(new ZipEntry(filedisplay)); } curLatnCode = rs.getString("LATN_ID"); zout.write(rs.getString("OFR_DEV_NUM").getBytes()); zout.write(splitChar.getBytes()); zout.write(lineEndChar.getBytes()); count++; } if(count==0){ filedisplay = "没有数据" + month_id + ".csv"; zout.putNextEntry(new ZipEntry(filedisplay)); } zout.flush(); if(zout!=null){ zout.close(); } if(rs!=null) rs.close(); stmt.close(); conn.close(); if (osPage != null) { osPage.close(); osPage = null; out.clear(); out = pageContext.pushBody(); } } catch (Exception e) { e.printStackTrace(); } %> , 问题扩展 我们在处理类似问题时,也可以参考上述几个关键问题的解决方式,通过输入输出流的 方式进行开发,形成数据获取、数据传输的并行处理。 比如,我们在处理集团ftp接口送数据的问题时,实现了一边查数据,一边ftp传输数 据的问题。而不是先生成接口数据文件,然后ftp他们 ,结果除了产生临时文件,还消耗 宝贵的时间。我们采用commons-net-1.4.1.jar包,直接ftp从 JDBC ResultSet对象中获取的 字节数据流,如果接口文件需要压缩同样可以借鉴上面的压缩部分。 JSP代码片段如下: …… String splitChar=","; String lineEndChar="\r\n"; DataSource ds = (DataSource) initial.lookup(strJDNI); conn = ds.getConnection(); stmt=conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); FtpUtil ftputil = new FtpUtil(); ftputil.connectServer("133.64.37.14", 21, "tbas", "tbas", "/home/tbas/web"); OutputStream os=null; os = ftputil.ftpClient.storeFileStream("B2008091.TBL_JQ_F_1.20081006.C0612300"); rs=stmt.executeQuery(sql); if(rs!=null){ while(rs.next()) { os.write(isNull(rs.getString("unit_code"))); os.write(splitChar.getBytes()); os.write(isNull(rs.getString("month_id"))); os.write(splitChar.getBytes()); os.write(lineEndChar.getBytes()); } os.close(); ftputil.ftpClient.completePendingCommand(); os =null; rs.close(); } ftputil.closeServer(); …… , 问题 我们可以在JSP循环获取数据的地方打印标记到控制台,进行验证数据获取、压缩及传输是否同时进行。从实际运行过程来看,的确是一边取数据一边压缩传送,达到预期目的,在实际项目使用过程中效果良好,十个本地网的清单数据共59.6M,被压缩成9.7M,压缩了83%,浏览器端下载速度在190K/S附近波动。如果通过方法ZipOutputStream.setLevel()设置压缩级别为9,还可以减少400K左右,可设置的压缩级别范围(0-9),实际使用过程中可以不进行设置。如果清单很大(百兆以上)不建议使用该方法,使用数据库导出方式以文件形式给用户提供。 , 结束语
/
本文档为【[要略]巧用jsp完成清双数据压缩下载】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索