zoukankan      html  css  js  c++  java
  • 多线程报表生成其中报表以pdf形式保存

         设计思路采用生产者消费者模式,生产者生产报表消费者消费报表生成pdf文件其中报表以html形式存储在线程安全列表中.使用到技术有:多线程协作,线程池,线程安全,html 生成pdf.

    一.生产者生成html模版,方式通过多线程将数据和html模版整合技术是使用freemarker.

    1.ValPdfProduce

    package hk.buttonwood.ops.report;
    
    import java.io.File;
    import java.util.HashMap;
    import java.util.List;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    import hk.buttonwood.ops.pdf.Msg;
    import hk.buttonwood.ops.pdf.PdfProducer;
    import hk.buttonwood.ops.portfolio.PortfolioService;
    import net.zweb.core.ioc.annotation.Inject;
    import net.zweb.core.ioc.annotation.Resource;
    import net.zweb.core.util.FileUtil;
    
    /**
     * @author maybo 2015年10月31日
     */
    @Resource
    public class ValPdfProduce {
    	private String root = "/home/maybo/myProject/";// 保存文件的根目录
    	private String tPath = "src/main/webapp/WEB-INF/templates/ops/report/cash_pdf.html";// html模版文件
    	public ThreadPoolExecutor producerPool;
    	public  PdfProducer pdfProducer;
    	@Inject
    	private PortfolioService portfolioService;// 投资组合服务用于获取所有列
    	private ValProducer producer;
    	public ThreadPoolExecutor getProducerPool() {
    		return this.producerPool;
    	}
    	public void setPortfolioService(PortfolioService portfolioService) {
    		this.portfolioService = portfolioService;
    	}
    
    	public ValPdfProduce(String root, String tPath, ValProducer producer) {
    		this.root = root;
    		this.tPath = tPath;
    		this.producer = producer;
    	}
    	public ValPdfProduce() {
    	}
    	/*
    	 * 生成pdf
    	 */
    	public void produce(List<Integer> portfolios,String from,String to,String root, String tPath, ValProducer producer) {
    		this.root=root;
    		this.tPath=tPath;
    		this.producer=producer;
    		int cpu = Runtime.getRuntime().availableProcessors();
    		Msg msg=new Msg();
    		//如果portfolio存在无需查询
    		if(portfolios==null||portfolios.size()<=0){
    		// 获取所有portfolio列表
    		 portfolios = this.portfolioService.findAll();
    		}
    		if (portfolios == null || portfolios.isEmpty()||portfolios.size()<=0) {
    			Msg.State state=new Msg.State();
    			state.setState(Msg.State.NO_DATA);
    			msg.add(state);
    			msg.setTotal(-1);
    			return;
    		}
    		pdfProducer = PdfProducer.newInstance();
    		LinkedBlockingQueue<HashMap<String, Object>> queue = pdfProducer.getCached();
    		String html = htmlToString(tPath);
    		if (html == null || html == "") {
    			Msg.State state=new Msg.State();
    			state.setState(Msg.State.NO_DATA);
    			msg.add(state);
    			msg.setTotal(-1);
    			return;
    		}
    		msg.setTotal(portfolios.size());
    		pdfProducer.setMsg(msg);
    		// 开启一个生产估值数据的线程池
    		producerPool = new ThreadPoolExecutor(cpu / 2, 500, 5, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(500));
    		for (Integer p : portfolios) {
    			if (p< 0) {//但没有值时返回状态
    				Msg.State state=new Msg.State();
    				state.setPortfolio(p);
    				state.setState(Msg.State.NO_DATA);
    				msg.add(state);
    				continue;
    			}
    			producerPool.execute(new ValProduceTask( msg,queue, p,html, root, this.producer,from,to));
    			try {
    				pdfProducer.addTask(null);
    			} catch (Exception e) {
    
    				e.printStackTrace();
    			}
    		}
    		try {
    
    			pdfProducer.shutDown();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		producerPool.shutdown();
    	}
    
    	/*
    	 * 将html和数据整合到一起
    	 * 
    	 * @param:html文件路径
    	 * 
    	 */
    	private String htmlToString(String htmlFile) {
    		String html = "";
    		File file = new File(htmlFile);
    		if (file.exists() && file.isFile()) {
    			html = FileUtil.readFile(file);
    		}
    		return html;
    	}
    }
    

     2.

    package hk.buttonwood.ops.report;
    
    import java.util.Map;
    
    import hk.buttonwood.ops.pdf.ExcTask;
    
    /**
     * @author maybo 2015年10月31日
     */
    public abstract class ValProducer{
    	/*
    	 * 将月结单数据保存到数据模版中
    	 * 
    	 * @param:portfolio中id
    	 * 
    	 * @param:保存文件的根目录
    	 * 
    	 * @param:htmlFile的位置
    	 * @param:更新日期
    	 */
    	protected abstract Map<String, Object> valToPdfTemplate(int portfolio,String root, String html,String date);
    	/*
    	 * 将月结单数据保存到数据模版中
    	 * 
    	 * @param:portfolio中id
    	 * 
    	 * @param:保存文件的根目录
    	 * 
    	 * @param:htmlFile的位置
    	 * 
    	 * @param:更新日期
    	 * @param:截至日期
    	 */
    	protected abstract Map<String, Object> valToPdfTemplate(int portfolio, String root, String html,String from,String to);
    }
    

      3.生产任务

    package hk.buttonwood.ops.report;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.LinkedBlockingQueue;
    
    import hk.buttonwood.ops.pdf.Msg;
    
    /*
     * 建立一个获取数据的线程
     */
    /**
     * @author maybo
     *	2015年10月31日
     */
    public class ValProduceTask extends Thread {
    	// 线程安全队列用以存储数据
    	private LinkedBlockingQueue<HashMap<String, Object>> queue;
    	private int portfolio;// 投资组合的id
    	private String from;// 当前日期
    	private String html;// html模版
    	private String root;// 存储的根目录
    	private ValProducer valProducer;// 获取相应的数据
    	private String to;// 截至日期日期
    	private Msg msg;//执行状态
    	/*
    	 * 传递所需数据
    	 * 
    	 * @param:缓存队列
    	 * 
    	 * @param:投资组合id
    	 * 
    	 * @param:查询日期
    	 * 
    	 * @param:html模版
    	 * 
    	 * @param:提供的数据获取方法有调用者实现传递
    	 * 
    	 * @param:跟目录
    	 * 
    	 * @保存文件名
    	 */
    
    	public ValProduceTask(Msg msg,LinkedBlockingQueue<HashMap<String, Object>> queue, int portfolio,String html,
    			String root, ValProducer valProducer,String from,String to) {
    		this.queue = queue;
    		this.from = from;
    		this.html = html;
    		this.to=to;
    		this.portfolio = portfolio;
    		this.root = root;
    		this.valProducer = valProducer;
    		this.msg=msg;
    	}
    
    	@Override
    	public void run() {
    		synchronized (queue) {
    			Map<String, Object> data;
    			if(to==null){
    				 data = valProducer.valToPdfTemplate(portfolio,root, html, from);
    			}else{
    			 data = valProducer.valToPdfTemplate(portfolio,root, html, from,to);
    			}
    			if (data != null) {
    				queue.add((HashMap<String, Object>) data);
    			}else{
    				Msg.State state=new Msg.State();
    				state.setPortfolio(this.portfolio);
    				state.setState(Msg.State.NO_DATA);
    				msg.add(state);
    			}
    			queue.notifyAll();
    		}
    	}
    }
    

     二.消费者也就是pdf的生产者消费html模版生成pdf.

    1.底层类实现将html to pdf.

    /*
     * To change this template, choose Tools | Templates
     * and open the template in the editor.
     */
    package zweb.plugins.pdf;
    
    /**
     *
     * @author john
     */
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.util.HashMap;
    import java.util.Map;
    
    import net.zweb.core.config.Config;
    import net.zweb.core.config.MapConfig;
    import net.zweb.core.util.FileUtil;
    
    import org.apache.commons.lang.StringUtils;
    
    import com.itextpdf.text.BaseColor;
    import com.itextpdf.text.Document;
    import com.itextpdf.text.Font;
    import com.itextpdf.text.PageSize;
    import com.itextpdf.text.pdf.PdfWriter;
    import com.itextpdf.tool.xml.XMLWorkerFontProvider;
    import com.itextpdf.tool.xml.XMLWorkerHelper;
    
    public class HtmlToPDF {
    	
    	private HeaderFooter headerFooter = null;
    	
    	public HtmlToPDF(){
    		
    	}
    	
    	public HtmlToPDF(HeaderFooter headerFooter){
    		this.headerFooter = headerFooter;
    	}
    	
    	public String print(String html, Map<String, Object> data, String path) throws Exception {
    		try {
    			
    			Document document = null;
    			if(data!=null){
    				Config<String, Object> cfg = new MapConfig<String, Object>(data);
    				String pageSize = cfg.getString("pageSize");
    				String direction = cfg.getString("direction");
    				if(StringUtils.isBlank(pageSize)){
    					pageSize = "letter";
    				}
    				if(StringUtils.isBlank(direction))
    					direction = "verticle";
    				if(StringUtils.equals(direction, "verticle"))
    					document = new Document(PageSize.getRectangle(pageSize)); // 竖向打印
    				else
    					document = new Document(PageSize.getRectangle(pageSize).rotate()); // 横向打印
    			}else{
    				document = new Document(PageSize.LETTER);
    			}
    			PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream(path));
    			
    			if (this.headerFooter != null)
    				pdfWriter.setPageEvent(headerFooter);
    
    			document.open();
    			document.addAuthor(String.valueOf(data.get("author")));
    			document.addCreator(String.valueOf(data.get("creator")));
    			document.addSubject(String.valueOf(data.get("subject")));
    			document.addCreationDate();
    			document.addTitle(String.valueOf(data.get("title")));
    			XMLWorkerHelper worker = XMLWorkerHelper.getInstance();
    			// worker.parseXHtml(pdfWriter, document, new StringReader(str));
    			worker.parseXHtml(pdfWriter, document, new ByteArrayInputStream(html.getBytes()), null, 
    				new XMLWorkerFontProvider(){
    					 @Override
    					  public Font getFont(final String fontname, final String encoding,
    							  final boolean embedded, final float size, final int style,final BaseColor color) {
    						  String fntname = fontname;
    				          if(fntname==null){
    				              fntname="宋体";
    				              fntname="微软雅黑";
    				        	 // fntname="uming";
    				          }
    				      return super.getFont(fntname, encoding, size, style);
    					 }
    				}
    			);
    			// worker.parseXHtml(pdfWriter, document, new
    			// ByteArrayInputStream(html.getBytes()));
    			document.close();
    		} catch (Exception e) {
    			throw e;
    		}
    		return null;
    	}
    
    	public static void main(String... args) throws Exception {
    		String root = "src/main/webapp";
    		HeaderFooter headerFooter = new HeaderFooter(root);
    		headerFooter.setPrintDirection("horizon");
    		headerFooter.setX(-30);
    		headerFooter.setY(630);
    		headerFooter.setImg("/images/print_holder.png");
    		HtmlToPDF toPdf = new HtmlToPDF(headerFooter);
    		String path = "d:/testpdf3.pdf";
    		//String str = FileUtil.readFile(new File("src/main/webapp/WEB-INF/templates/custody/fund/voucher/print.html"));
    		String str = FileUtil.readFile(new File("src/main/webapp/WEB-INF/templates/register/holder/print_bond.html"));
    		Map<String, Object> data = new HashMap<String, Object>();
    		data.put("author", "author");
    		data.put("creator", "creator");
    		data.put("subject", "subject");
    		data.put("title", "title");
    		data.put("direction", "horizon");
    		data.put("pageSize", "A4");
    		toPdf.print(str, data, path);
    	}
    
    }
    

      2.PdfPrint 基本执行生成pdf.

    package hk.buttonwood.ops.pdf;
    
    import java.util.Map;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import net.zweb.core.mvc.template.AbstractTemplateEnginePlugin;
    import net.zweb.core.mvc.template.TemplateEngine;
    import zweb.plugins.pdf.HeaderFooter;
    import zweb.plugins.velocity.VelocityPlugin;
    
    public class PrintPdf implements ExcTask {
    	private Map<String, Object> data;
    	private static TemplateEngine engine;
    	private HeaderFooter headerFooter;
    	private Logger logger=LoggerFactory.getLogger(PdfTask.class);
    	static {
    		AbstractTemplateEnginePlugin plugin = new VelocityPlugin();
    		engine = plugin.templateEngine();
    	}
    
    	public PrintPdf(Map<String, Object> data,HeaderFooter headerFooter) {
    		this.data = data;
    		this.headerFooter=headerFooter;
    	}
    
    	@SuppressWarnings("unchecked")
    	@Override
    	public void excute() throws Exception {
    		String html = "";
    		html = htmlToString((String) data.get("html"), (Map<String, Object>) data.get("datas"));
    		if ("".equals(html)) {
    			return;
    		}
    		new BuildPdfUtil(this.headerFooter).build(html, (String) data.get("saveFile"), (Map<String, Object>) data.get("datas"));
    		logger.debug((String)data.get("saveFile")+"---------------完成");
    	}
    
    	/*
    	 * 将html和数据整合到一起
    	 * 
    	 * @param:html文件路径
    	 * 
    	 * @param:数据模型
    	 */
    	private String htmlToString(String html, Map<String, Object> datas) {
    		String htl = "";
    		htl = engine.render(html, datas);
    		return htl;
    	}
    
    }
    

     3.线程任务生成pdf调用PdfPrint

    package hk.buttonwood.ops.pdf;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.LinkedBlockingQueue;
    import zweb.plugins.pdf.HeaderFooter;
    
    /**
     * @author maybo 2015年10月28日 估值报告线程任务
     */
    public class PdfTask extends Thread {
    	// 线程安全队列用以存储数据
    	private LinkedBlockingQueue<HashMap<String, Object>> queue;
    	private HeaderFooter headerFooter;
    	private   Msg msg;
    	public PdfTask(LinkedBlockingQueue<HashMap<String, Object>> queue,HeaderFooter headerFooter,Msg msg) {
    		this.queue = queue;
    		this.headerFooter=headerFooter;
    		this.msg=msg;
    	}
    
    	@Override
    	public void run() {
    		synchronized(queue){
    		while (queue.size() <= 0) {
    			try {
    				queue.wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    				queue.notifyAll();
    			}
    		}
    		Map<String, Object> data = queue.poll();
    		queue.notifyAll();
    		try {
    			ExcTask excTask = new PrintPdf(data,headerFooter);
    			excTask.excute();
    			Msg.State state=new Msg.State();
    			if(data.get("portfolio")!=null){
    			state.setPortfolio((Integer)data.get("portfolio"));
    			state.setState(Msg.State.OK);
    			msg.add(state);
    			}			
    		} catch (Exception e1) {
    			Msg.State state=new Msg.State();
    			if(data.get("portfolio")!=null){
    			state.setPortfolio((Integer)data.get("portfolio"));
    			state.setState(Msg.State.ERROR);
    			msg.add(state);
    			}
    			e1.printStackTrace();
    		}
    	}
    	}
    }
    

      4.PdfProducer //也就是消费者

    package hk.buttonwood.ops.pdf;
    import java.util.HashMap;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    import zweb.plugins.pdf.HeaderFooter;
    
    public class PdfProducer {
    	private LinkedBlockingQueue<HashMap<String, Object>> queue;
    	private ThreadPoolExecutor pool;
    	private Msg msg;
    	private PdfProducer() {
    	}
    
    	public static PdfProducer newInstance() {
    		PdfProducer pdfProducer = new PdfProducer();
    		int cpu = Runtime.getRuntime().availableProcessors();// 获取cpu核数
    		pdfProducer.queue = new LinkedBlockingQueue<HashMap<String, Object>>();// 创建队列用于缓存任务
    		// 开启一个生产估值数据的线程池
    		pdfProducer.pool = new ThreadPoolExecutor(cpu / 2-1<=0?1:cpu/2-1, 500, 5, TimeUnit.MINUTES,
    				new ArrayBlockingQueue<Runnable>(500));// 开启线程池
    		return pdfProducer;
    	}
    
    	public LinkedBlockingQueue<HashMap<String, Object>> getCached() {
    		return queue;
    	}
    	public void setMsg(Msg msg) {
    	this.msg = msg;
    }
    	public Msg getMsg() {
    		return this.msg;
    	}
    	// 关闭线程池
    	public void shutDown() {
    		this.pool.shutdown();
    	}
    
    	// 现在关闭
    	public void shutDownNow() {
    		this.pool.shutdownNow();
    	}
    
    	// 关闭状态
    	public boolean isShutDown() {
    		return this.pool.isShutdown();
    	}
    	// 是否完成任务
    	public boolean isTerminated() {
    		return this.pool.isTerminated();
    	}
    
    	public boolean isTerminating() {
    		return this.pool.isTerminating();
    	}
    
    	/*
    	 * 添加任务数
    	 * param:pdf的页眉
    	 */
    	public void addTask(HeaderFooter footer) throws Exception {
    		if (this.isShutDown()) {
    			throw new Exception("已经关闭任务.");
    		}
    			pool.execute(new PdfTask(queue,footer,msg));
    	}
    
    }
    

      

  • 相关阅读:
    剑指offer--26.顺时针打印矩阵
    剑指offer--25.二叉树的镜像
    剑指offer--24.树的子结构
    剑指offer--23.合并两个排序的链表
    剑指offer--22.反转链表
    剑指offer--21.链表中倒数第k个结点
    剑指offer--20.矩形覆盖
    剑指offer--19.重建二叉树
    剑指offer--18.从尾到头打印链表
    剑指offer--17.第一个只出现一次的字符
  • 原文地址:https://www.cnblogs.com/maybo/p/5182528.html
Copyright © 2011-2022 走看看