最近因爲工做上的須要,須要作一個簡歷產品的下載功能,而下載的形式要去爲PDF,內容要求爲整個簡歷的內容,並且格式上要求和簡歷的格式排版時一致的!前期調研、開發,最後測試上線,差很少花了7天的時間,固然,期間主要完成了主體功能,如今的話,該功能已經至關完善。下面,我主要是總結下我在這個開發的過程當中遇到的問題和總結的心得,但願能幫組有這方面須要的人。html
原創文章,轉載請註明出處:http://blog.csdn.net/jessonlvjava
前期調研的時候,在網上看了不少關於轉pdf的相關文章和技術框架,詳細的我不想在此一一贅述,整體給個人感受就是,第一:國外的相關技術框架作的就是好,關於這方面的,基本都是國外的技術,最多也就是國內牛人改改源碼,來適應中文等相關的本土化須要。第二:國內有關生產pdf的需求通常都很簡單,要麼就是簡單文本,複雜的最多也就是相關報表等,基本麼有本身想要實現的那麼複雜的內容、排版。尤爲是生成的內容要和也也面上的內容徹底一致,樣式排版徹底一致!web
一、首先經過相關功能接口,取出這個簡歷的全部數據。json
二、經過freemarker排版輸出的html靜態頁面。靜態頁面的樣式決定了生成的pdf的樣式。瀏覽器
三、讀取html靜態頁面,轉換成pdf。tomcat
四、將pdf輸出在瀏覽器,實現下載功能。app
<#assign freemarkerTool= "com.shengao.mojianli.util.FreemarkerTool"?new()> <html> <head> <title>mojianli</title> <style> @font-face { font-family: 'Microsoft YaHei'; font-family: 'Arial'; } html, body, p { margin: 0; padding: 0; } span { line-height: 1px; } body { font-family: 'Microsoft YaHei'; font-size: 11px; color: #666666; } .wrapper { width: 900px; margin: 0 auto; } .block { margin-top: 20px;//+2 } .align-center { text-align: center; margin-left: 252px; width: 200px; } #name { font-size: 25px; margin-top: 0px; color: #333333; margin-bottom: 0; } #phone { font-size: 12px; margin-top: 12px; font-family: "Arial"; } #email { font-size: 11px; margin-top: 4px; font-family: "Arial"; } .timestamp { display: inline-block; width: 110px; } .title { font-size: 13px; } .simple-module { margin-bottom: 10px; font-size: 11px; } .simple-module-item{ font-size: 11px; margin-right: 16px; } .simple-item { font-size: 11px; margin-right: 16px; } .item_thing { font-size: 11px; margin-right: 6px; line-height: 18px; } .label { background: #666; border-radius: 1px; color: white; font-size: 7px; position: relative; top: -1px; line-height: 7px; } .product { margin-left: 150px; margin-bottom: 5px; margin-top: 4px; width:750px; } .capacity-block { margin-left: 22px; } .things { padding-left: 12px; margin-top: 5px; background: url(img/dot.png) left top no-repeat; } .tag { background: #333333; border-radius: 1px; color: white; font-size: 11px; padding: 0 1px 1px 1px; margin-right: 4px; border-radius: 1px; } .enterprise{ margin-bottom: 9px; } .enterprise .simple-item { margin-bottom: 5px; } .capacity-group { margin-top: 5px; width: 520px; } .paragraph { line-height: 16px; font-size: 11px; margin-bottom: 10px; width: 700px; } .h-seperator { border-color: #666; margin-top: 7px; height: 0; border-top: none; border-bottom: 1px solid; margin-bottom: 6px; } .company,.department,.title{ color: #333; } </style> </head> <body> <#escape x as x!""></#escape> <div class="wrapper"> <div class="block align-center" id="contact"> <p id="name">${name}</p> <p id="phone">${phone}</p> <p id="email">${email}</p> </div> <div class="block" id="education"> <span class="title">${exp}</span> <div class="h-seperator"></div> <#list education as education> <p class="simple-module"> <span class="timestamp simple-module-item">${education.start_date} ~ ${education.end_date}</span> <span class="simple-module-item"><#if education.university??>${education.university}</#if></span> <span class="simple-module-item"><#if education.colleges??>${education.colleges}</#if> · <#if education.major??>${education.major}</#if></span> <span class="simple-module-item"><#if education.degree??>${education.degree}</#if></span> <span class="simple-module-item"><#if education.explain??>${education.explain}</#if></span> </p> </#list> </div> <!--under--> <!--割一割--> <div class="block" id="experience"> <span class="title">${project}</span> <div class="h-seperator"></div> <!--項目模板代碼開始--> <!--項目經歷開始--> <#list experience as experiences> <div class="enterprise"> <span class="timestamp simple-item">${experiences.experience.start_date} ~ ${experiences.experience.end_date}</span> <span class="simple-item company">${experiences.experience.company}</span> <span class="simple-item department">${experiences.experience.department}</span> <span class="simple-item title">${experiences.experience.title}</span> <!--項目名稱開始--> <#list experiences.projects as projects> <div class="product"> <span class="simple-item">${projects.project.name}</span> <span class="simple-item">${projects.project.phase}</span> <span class="simple-item">${projects.project.core_goal}</span> <!--標籤、事情開始--> <#list projects.tags as tags> <div class="capacity-block"> <div class="capacity-group"> <!--標籤開始--> <#list tags.tags as tag> <span class="tag">${tag.base_tag_name}</span> </#list> <!--標籤結束--> <!--事情開始--> <#list tags.items as item> <div class="things"> <#list item.labels as label> <span class="label">${label.base_label_name}</span> <span class="item_thing">${label.content}</span> </#list> </div> </#list> <!--事情結束--> </div> </div> </#list> <!--標籤、事情結束--> </div> </#list> <!--項目名稱結束--> </div> </#list> <!--項目名稱結束--> <!--項目模板代碼結束--> </div> <!--割一割--> <div class="block" id="honor"> <span class="title">${awards}</span> <div class="h-seperator"></div> <#list awardses as awardses> <p class="simple-module"> <span class="simple-item timestamp">${awardses.start_date} ~ ${awardses.end_date}</span> <span class="simple-item"><#if awardses.name??>${awardses.name}</#if></span> <span class="simple-item"><#if awardses.level??>${awardses.level}</#if></span> <span class="simple-item"><#if awardses.rank??>${awardses.rank}</#if></span> <span class="simple-item"><#if awardses.number??>${awardses.number}</#if></span> </p> </#list> </div> <div class="block" id="evaluation"> <span class="title">${evaluate}</span> <div class="h-seperator"></div> <#list evaluates as evaluates> <p class="paragraph">${freemarkerTool(evaluates.content)}</p> </#list> </div> </div> </body> </html>
@RequestMapping(value = "/createPdf.s", method = {RequestMethod.POST,RequestMethod.GET}) public void getAllResumeInfoById(HttpServletRequest request, HttpServletResponse response, @RequestParam(value="id", required = true) Long id) { String perName = ""; String positionName = ""; long resumeId = id; //獲取全部的數據 //我的基本信息 ResumeInfoBean resumeInfo = new ResumeInfoBean(); //教育經歷 List<EducationBean> eduList = new ArrayList<EducationBean>(); //獲獎經歷 List<AwardsBean> awardsList = new ArrayList<AwardsBean>(); //我的評價 List<EvaluateBean> evaList = new ArrayList<EvaluateBean>(); //項目經歷 List<PdfExperience> pdfExperience = new ArrayList<PdfExperience>(); try { Map<String, Object> map = resumeInfoService.getAllResumeInfoById(id); resumeInfo = (ResumeInfoBean)map.get("resumeInfo"); eduList = (List<EducationBean>)map.get("education"); awardsList = (List<AwardsBean>)map.get("awards"); evaList = (List<EvaluateBean>)map.get("evaluates"); pdfExperience = (List<PdfExperience>)map.get("experiences"); System.out.println("finish...pdfExperience.size=="+pdfExperience.size()); } catch (Exception e) { log.warn(e); JsonUtil.errorToClient(response, 400, e.getMessage()); return; } //工程路徑 String path = request.getSession().getServletContext().getRealPath("/"); try { Configuration cfg = new Configuration(); cfg.setDirectoryForTemplateLoading(new File(getPath(request,response))); cfg.setObjectWrapper(new DefaultObjectWrapper()); cfg.setDefaultEncoding("UTF-8"); //這個必定要設置,否則在生成的頁面中會亂碼 //設置對象包裝器 cfg.setObjectWrapper(new DefaultObjectWrapper()); //設計異常處理器 cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); //準備魔簡歷數據 Map<String, Object> ResumeMap = new HashMap<String, Object>(); //頭部信息bean對象 String name = resumeInfo.getName(); perName = name; positionName = resumeInfo.getBase_position_name(); String phone = resumeInfo.getMobile(); String email = resumeInfo.getEmail(); ResumeMap.put("name",name); ResumeMap.put("phone",phone); ResumeMap.put("email",email); //定義四個模塊標題 ResumeMap.put("exp","教育經歷"); ResumeMap.put("project","項目經歷"); ResumeMap.put("awards","獲獎經歷"); ResumeMap.put("evaluate","我的評價"); //封裝教育經歷對象數據 ResumeMap.put("education", eduList); String streducationlist = JsonUtil.list2json(eduList); //封裝項目經歷數據 ResumeMap.put("experience", pdfExperience); String strEx= JsonUtil.list2json(pdfExperience); System.out.print(strEx); //封裝獲獎經歷數據 ResumeMap.put("awardses",awardsList); String strawardsList = JsonUtil.list2json(awardsList); //封裝我的評價數據 ResumeMap.put("evaluates",evaList); String strevaList = JsonUtil.list2json(evaList); //獲取指定模板文件 Template template = cfg.getTemplate("mojianli.ftl"); //控制檯打印 template.process(ResumeMap, new PrintWriter(System.out)); //定義輸入文件,默認生成在工程根目錄 String s = getPath(request,response); path = s+id+"_mojianli.html"; Writer out = new OutputStreamWriter(new FileOutputStream(path),"UTF-8"); //最後開始生成 template.process(ResumeMap, out); System.out.println("create the html successful!!!"+"path="+request.getSession().getServletContext().getRealPath("/")); }catch (Exception e){ e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } boolean boo = false; try { boo = html2Pdf(request,response,perName,resumeId,positionName); }catch (Exception e){ e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } }
//html轉成pdf private boolean html2Pdf(HttpServletRequest request,HttpServletResponse response,String name,long id,String postionName) throws IOException, DocumentException, ParserConfigurationException { boolean bl = false; //工程路徑 /*String separator = File.separator; String root = request.getSession().getServletContext().getRealPath(""); String path = root+separator+"WEB-INF"+separator+"resources"+name+"_"+id+"_mojianli.html";*/ String path = getPath(request,response)+id+"_mojianli.html"; //獲取已經生成的html頁面的路徑 //String path = "F:\\tomcat_myeclipse\\webapps\\mojianli\\WEB-INF\\resources\\mojianli.html"; //讀取html FileInputStream fis =new FileInputStream(path); StringWriter writers = new StringWriter(); InputStreamReader isr = null; String string = null; //此處將io流轉換成String try { isr = new InputStreamReader(fis,"utf-8");//包裝基礎輸入流且指定編碼方式 //將輸入流寫入輸出流 char[] buffer = new char[2048]; int n = 0; while (-1 != (n = isr.read(buffer))) { writers.write(buffer, 0, n); } }catch (Exception e){ e.printStackTrace(); } finally { if (isr != null) try { isr.close(); } catch (IOException e) { e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } } if (writers!=null){ string = writers.toString(); } System.out.print(string); //利用renderer來準備數據 ITextRenderer renderer = new ITextRenderer(); ITextFontResolver fontResolver = renderer.getFontResolver(); //設置建立PDF的時候要用的字體,此字體必需要和簡歷模板的字體保持一致!! fontResolver.addFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); fontResolver.addFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); //get font family name BaseFont font = null; BaseFont font2 = null; try { font = BaseFont.createFont(getPath(request, response)+"msyh.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); font2 = BaseFont.createFont(getPath(request, response)+"arial.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); } catch (DocumentException e) { e.printStackTrace(); jsonObjOutPut.clear(); jsonObjOutPut = JsonUtil.createJsonObject(MSG.STATUS_RESPONSE_FAIL_201, MSG.MSG_RESPONSE_FAIL_201); stringOutPutData = JsonUtil.object2json(jsonObjOutPut); JsonUtil.jsonStringToClient(response,stringOutPutData); } //fontFamilyName‘s value is the key for font-family String fontFamilyName = TrueTypeUtil.getFamilyName(font2); System.out.println("fontFamilyName222="+fontFamilyName); //設置pdf內容!! renderer.setDocumentFromString(string); //設置圖片的絕對路徑 renderer.getSharedContext().setBaseURL("file:"+getPath(request,response)+"\\img"); System.out.println(getPath(request,response)+"img"); renderer.layout(); //create the pdf //String pdfPath = path+"WEB-INF\\resources\\"+name+"_mojianli.pdf"; String pdfPath = getPath(request, response)+id+"_mojianli.pdf"; FileOutputStream outputStream = new FileOutputStream(pdfPath);//文件輸出根目錄下 renderer.createPDF(outputStream); //Finishing up //renderer.finishPDF(); System.out.println("created the pdf !!"); //下載 try{ //downloadPdf(response,request,name,outputStream); downLoadPdf(request,response,name,id,postionName); }catch (Exception e ){ e.printStackTrace(); } bl = true; return bl; }
public void downLoadPdf(HttpServletRequest request, HttpServletResponse response,String name,long id,String postionName) { try { String separator = File.separator; String root = request.getSession().getServletContext().getRealPath(""); String filePath = root+separator+"WEB-INF"+separator+"resources"; String headerName = new String(name.getBytes("utf-8"),"iso8859_1");//解決下載文件中文標題亂碼問題 String postion = new String(postionName.getBytes("utf-8"),"iso8859_1"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename="+headerName+"-"+postion+".pdf"); OutputStream outputStream = response.getOutputStream(); InputStream inputStream = new FileInputStream(filePath + separator+id+"_mojianli.pdf"); byte[] buffer = new byte[1024]; int i = -1; while ((i = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, i); } outputStream.flush(); //outputStream.close(); inputStream.close(); } catch (Exception e) { e.printStackTrace(); log.warn(e); JsonUtil.errorToClient(response, 400, e.getMessage()); return; } }這裏的注意點是:注意下載文件中文標題亂碼問題 至此,以上整體的代碼大概是這樣,須要的人,能夠多看看,若是有什麼問題,歡迎隨時私信、留言等交流。