Java解析OFFICE(word,excel,powerpoint)以及PDF的实现⽅案。。。
Java解析OFFICE(word,excel,powerpoint)以及PDF的实现⽅案及开发中的点滴分享
  在此,先分享下写此⽂前的经历与感受,我所有的感觉浓缩到⼀个字,那就是:"坑",如果是两个字那就是"巨坑"=>因为这个需求⼀开始并不是这样⼦的,且听我漫漫道来:
  ⼀开始客户与我们商量的是将office和PDF上传,将此类⽂件解析成html格式,在APP端调⽤内置server直接以html"播放"
  经历⼀个⽉~,两个⽉~,三个⽉~~~
  到需求开发阶段,发现这是个坑。。。:按照需规的意思这个整体是当做⼀个功能来做的,技术难度也就算了,⽽且按照估算的⼯时也很难做成需规所需要的样⼦(缺陷太多!)
  然后⼀周~,⼀周~,⼜⼀周~~~
  各种⽅案下来将需求做成能⽤的样⼦,然后需求确认时客户说:“我们没有要求你们能解析这些⽂档,我们只要求你们当做⼀个源⽂件上传,在APP端点击直接能选择调⽤第三⽅应⽤打开就⾏了,⽽且⼀开始我们的需求就是这样的。”
  /**听完,顿时泪流满⾯( _ ),如果业务⼀开始就确认这样做,何⾄于浪费如此多的时间,花费如此多的精⼒绕⽼⼤⼀圈。。。*/java修改html文件
  需求绕了⼀圈⼜绕回来了,作为经历过的⼈,现在总结下这需求⾥⾯⽆尽的坑:
  A>开源社区有很多Demo,这些Demo有很多缺陷,⽐如office⾥⾯的艺术字、图⽚、公式、颜⾊样式、视频和⾳频不能解析
  B>能解析的对象,解析出来的效果不是很好,⽐如word和ppt⾃⾝的排版乱了,excel单元格⾥⾯的⾃定义格式全变成数字了~等等
  C>开源社区的资料并不是很全,导致的结果是不同的⽂档类型需要⽤不同的解析⽅式去解析,⽐如word⽤docx4j解析、excel⽤poi解析带来的代码量巨⼤
  D>由于代码⾃⾝的解析效果不是很好,更改后的⽅案需要在上传之前将源⽂件处理成其他的形式,如pdf需要切成图⽚,ppt需要转换成视频或是图⽚,这样⼀来需求实现的⽅式就变成半⾃动了╥﹏╥...
  E>word⽤docx4j解析⼀个很⼤的问题是解析的效率太低了,5MB以上的⽂件或者内容⽐较复杂的word⽂档解析⼗分耗时,解析效率太低,再⼀就是poi解析数据量⽐较⼤的Exel(⽐如>1000⾏)容易造成内存溢出,不好控制
  F>⼯时太短,只有15天。。。,加班加点(⊙︿⊙) ,包⼯头,加⼯资ε=怒ε=怒ε=怒ε=怒ε=( o`ω′)ノ
以上吐槽完了,该展⽰下最终成果了~
上4图从左⾄右依次是pdf、ppt、word、excel的解析html的效果,由于涉及开发协议上图1和图2部分地⽅有涂抹,且以上只是浏览器模拟⼿机显⽰,遂显⽰效果较为粗糙,在此⼗分抱歉~
下⾯介绍⼀下我的最终实现思路:
  A>Word⽂档分两种格式(03版)doc和(07版)docx,由于doc属于即将淘汰的格式同时为⽅便使⽤docx4j⼀步到位的实现⽅式,故不考虑doc格式⽂档
  B>同Word⼀样,excel也不考虑旧版格式的转换,⽅案是选⽤第三⽅Demo实现,涉及到具体的技术就是 poi.hssf
  C>PowerPoint(ppt)由于内置对象⽐较多,为保证客户的使⽤体验,我的⽅案是将ppt直接导出成mp4或图⽚(需打zip包)上传,再⽤代码包装成html
  D>对于pdf,同样没有很好的Demo实现成html,遂同ppt⼀样通过软件转换成图⽚的形式打包上传,再⽤代码包装成html
先展⽰下word解析的相关代码:
(代码⽚段⼀)
1public static void Word2Html() throws FileNotFoundException, Docx4JException{
2//需在log4j内配置docx4j的级别
3            WordprocessingMLPackage wmp = WordprocessingMLPackage.load(new File("C:\\Users\\funnyZpC\\Desktop\\Test\\word.docx"));
4            HTML(wmp, "C:\\Users\\funnyZpC\\Desktop\\result\\wordIMG", "wordIMG", new FileOutputStream(new File("C:\\Users\\funnyZpC\\Desktop\\result\\word.html")));
5    }
(代码⽚段⼆)
1public ProcessFileInfo processDOCX(File file,String uploadPath)throws Exception{
2        String Name().substring(Name().lastIndexOf("."));//获取⽂件名称
3        WordprocessingMLPackage wmp = WordprocessingMLPackage.load(file);//加载源⽂件
4        String basePath=String.format("%s%s%s", uploadPath,File.separator,fileName);//基址
5        FileUtils.forceMkdir(new File(basePath));//创建⽂件夹
6        String zipFilePath=String.format("%s%s%s.%s", uploadPath,File.separator,fileName,"ZIP");//最终⽣成⽂件的路径
7        HTML(wmp, String.format("%s%s%s", basePath,File.separator,fileName),fileName,new FileOutputStream(new File(String.format("%s%s%s", basePath,File.separator,"index.html"))));//解析
8        scormService.zip(basePath, zipFilePath);//压缩包
9        FileUtils.forceDelete(new File(basePath));//删除临时⽂件夹
10        file.delete();//解析完成,删除原docx⽂件
11return new ProcessFileInfo(true,new File(zipFilePath).getName(),zipFilePath);//返回⽬标⽂件相关信息
12    }
解析word(docx)⽂档所需要的代码简单到只需要两⾏代码(代码⽚段⼀3、4两⾏),以上(代码⽚段⼆)是实际开发的代码,建议对⽐⽚段⼀看,同时由于项⽬可能会部署在linux系统下,建议使
⽤File.separator来代替"/"或者"\"路径分隔符;同时,需要解释的是toHtml⽅法的四个参数==>
  Html(加载源docx⽂件的WordprocessingMLPackage实例化对象,存放解析结果(html和图⽚)的基⽬录,存放图⽚的⽂件夹名称(在基⽬录下),输出主html的输出流对象);
下图是输出的结果的⽬录:
由于docx4j内部的log较多,默认Demo测试的时候输出⽂件会有如下提⽰:
这句话的⼤意是:如需隐藏此消息,请设置docx4j的debug的级别。解决的⽅式是在实际项⽬的log4j.properties中添加docx4j的消息级别为ERROR,如:
如果使⽤maven管理项⽬,直接在l⾥⾯添加docx4j的dependency,如果需⼿动配置docx4j及其依赖包,⼀定要注意依赖包与当前docx4j的版本对应性(推荐3.3.5的docx4j,解析效果
会好⼀些!)否则各种⽑病啊~,下图是maven仓库的⼀些说明,如需⼿动配置依赖⼀定要看下:
下⾯的代码是Excel解析word的部分代码⽚段(代码不全,如有需要请邮件私我):
(代码⽚段⼀)
1/**
2    *
3    * @param file                    源⽂件:c://xx//xx.xlsx
4    * @param uploadPath    基⽬录地址
5    * @return
6    * @throws Exception
7*/
8public ProcessFileInfo processXLSX(File file,String uploadPath)throws Exception {
9        List<String> Path());
10        FileUtils.forceMkdir(new File(uploadPath));//创建⽂件夹
11        String Name().substring(Name().lastIndexOf("."));//⽂件名称
12        String basePath=String.format("%s%s%s", uploadPath,File.separator,code);
13        FileUtils.forceMkdir(new File(basePath));
14        File htmlFile = new File(String.format("%s%s%s", basePath,File.separator,"index.html"));
15        Writer fw=null;
16        PrintWriter bw=null;
17//构建html⽂件
18try{
19              fw= new BufferedWriter( new OutputStreamWriter(new Path()),"UTF-8"));
20              bw=new PrintWriter(fw);
21//添加表头及可缩放样式
22            String head="<!DOCTYPE html><html><head><meta charset=\"UTF-8\"></head><body style=\"transform: scale(0.7,0.7);-webkit-transform: scale(0.7,0.7);\">";
23            StringBuilder body=new StringBuilder();
24for (String e : sheets) {
25                body.append(e);
26            }
27            String foot="</body></html>";
28            bw.write(String.format("%s%s%s", String(),foot));
29        }catch(Exception e){
30throw new Exception("");//错误扔出
31        }finally{
32if (bw != null) {
33                bw.close();
34            }
35if(fw!=null){
36                fw.close();
37            }
38        }
39        String htmlZipFile=String.format("%s%s%s.%s",uploadPath,File.Name().substring(Name().lastIndexOf(".")),"ZIP");
40//压缩⽂件
41        scormService.zip(basePath, htmlZipFile);
42        file.delete();//删除上传的xlsx⽂件
43        FileUtils.forceDelete(new File(basePath));
44return new ProcessFileInfo(true,new File(htmlZipFile).getName(),htmlZipFile);
45    }
View Code
(代码⽚段⼆)
1/**
2    * 程序⼊⼝⽅法
3    *
4    * @param filePath
5    *            ⽂件的路径
6    * @return <table>
7    *        ...
8    *        </table>
9    *        字符串
10*/
11public static List<String> readExcelToHtml(String filePath) {
12            List<String> htmlExcel=null;
13try {
14                File sourcefile = new File(filePath);
15                InputStream is = new FileInputStream(sourcefile);
16                Workbook wb = ate(is);
17                htmlExcel = getExcelToHtml(wb);
18            } catch (EncryptedDocumentException e) {
19                e.printStackTrace();
20            } catch (FileNotFoundException e) {
21                e.printStackTrace();
22            } catch (InvalidFormatException e) {
23                e.printStackTrace();
24            } catch (IOException e) {
25                e.printStackTrace();
26            }
27return htmlExcel;
28
29    }
View Code
以上只展⽰了xlsx⽂件的内容包装和解析excel的⼊⼝⽅法,整个解析类全部放在了utils包下⾯,service⾥⾯只管调⽤⽅法传参就好了,如下图:
解析Excel的⼯具类⼀共有四个⽂件类,其中Excel2HtmlUtils是⼊⼝类,其它三个均是关联Excel2HtmlUtils类处理Excel样式,需要注意的是:⼯具类处理Excel的时候⼀定要限制处理记录的数
量,以免造成内存溢出错误,顺便说下:如果您解析的html供移动端使⽤,建议给html设置可缩放⼤⼩=>transform: scale(0.7,0.7);-webkit-transform: scale(0.7,0.7);。
说完Excel解析,下⾯给出pdf(图⽚ZIP包)解析html的代码⽚段,由于代码较为简单,不多的解释,以
下是具体的实现代码:
1/**
2        * 根据⽂件名中的数字排列图⽚
3        *    a>提取⽂件名中的数字放⼊int数组(序列)
4        *  b>判断序列数组元素个数与⽂件个数是否⼀致,不⼀致则抛出
5        *  c>将序列数组从⼩到⼤排列
6        *  d>遍历序列数组获取Map中的⽂件名(value)并写html
7*/
8        String nm=null;
9int[] i=new int[imgNames.size()];
10        Map<Integer,String> names=new HashMap<Integer,String>();
11        Pattern p=Patternpile("[^0-9]");
12for(int j=0;j<imgNames.size();j++){
13            (j).substring((j).lastIndexOf("."));//提取名称
14            String idx=p.matcher(nm).replaceAll("").trim();
15            i[j]=Integer.parseInt("".equals(idx)?"0":idx);
16            names.put(i[j],(j));
17        }
18if(names.keySet().size()!=i.length){
19//System.out.println("====请检查您的图⽚编号====");/*重复或者不存在数字编号*/
20return new ProcessFileInfo(false,null,null);
21        }
22        Arrays.sort(i);//int数组内元素从⼩到⼤排列
23
24//包装成html
25        StringBuilder html=new StringBuilder();
26        html.append("<!DOCTYPE html><html><head><meta charset='UTF-8'><title>PDF</title></head>");
27        html.append("<body style=\"margin:0px 0px;padding:0px 0px;\">");
28for (int  k : i) {
29            html.append(String.format("%s%s%s%s%s","<div style=\"width:100%;\"><img src=\"./",fileName,File.(k),"\"  style=\"width:100%;\" /></div>"));
30        }
31        html.append("</body></html>");
32        File indexFile=new File(String.format("%s%s%s",basePath,File.separator,"index.html"));
33        Writer fw=null;
34        PrintWriter bw=null;
35//构建⽂件(html写⼊html⽂件)
36try{
37              fw= new BufferedWriter( new OutputStreamWriter(new FileOutputStream(indexFile),"UTF-8"));//以UTF-8的格式写⼊⽂件
38              bw=new PrintWriter(fw);
39              bw.String());
40        }catch(Exception e){
41throw new String());//错误扔出
42        }finally{
43if (bw != null) {
44                bw.close();
45            }
46if(fw!=null){
47                fw.close();
48            }
49        }
50        String zipFilePath=String.format("%s%s%s.%s", uploadPath,File.separator,file.hashCode(),"ZIP");
51        scormService.zip(basePath, zipFilePath);
52//删除⽂件
53        file.delete();
54        FileUtils.forceDelete(new File(basePath));
55return new ProcessFileInfo(true,new File(zipFilePath).getName(),zipFilePath);
56    }
View Code
同Excel,由于我将ppt存为mp4格式,上传后只需要做简单包装就可以了,处理的时候⼀定要注意html对视频的相对引⽤,以下是具体的实现代码:
1/**
2    *
3    * @param file                    上传的⽂件的路径 c://xx.//xxx.mp4
4    * @param uploadPath    保存html的基⽬录路径
5    * @return
6    * @throws Exception
7*/
8public ProcessFileInfo processPPTX(File file,String uploadPath)throws Exception{
9        String Name().substring(Name().lastIndexOf("."));//获取⽂件名称
10        String Name().Name().lastIndexOf(".")+Name().length()).toLowerCase();//⾳频⽂件后缀名
11        String basePath=String.format("%s%s%s", uploadPath,File.separator,fileName);
12        FileUtils.forceMkdir(new File(basePath));
13//将视频⽂件copy到basePath内
14        String videoPath=String.format("%s%s%s", basePath,File.Name());
15        pyFile(file, new File(videoPath));
16        StringBuilder html=new StringBuilder();
17        html.append("<!DOCTYPE html><html><head><meta charset='utf-8'><title>powerpoint</title></head>");
18        html.append("<body style=\"margin:0px 0px;\"><div style=\"width:100%;margin:auto 0% auto 0%;\">");
19        html.append("<video controls=\"controls\"  width=\"100%\"  height=\"100%\" name=\"media\" >");//⽆背景图⽚
20        html.append(String.format("%s%s.%s%s%s%s%s","<source src=\"",fileName,suffix,"\" type=\"audio/",suffix,"\" >","</video></div>"));//视频
21        html.append("</body></html>");//结尾
22        File indexFile=new File(String.format("%s%s%s",basePath,File.separator,"index.html"));
23        Writer fw=null;
24        PrintWriter bw=null;
25//构建⽂件(html写⼊html⽂件)
26try{
27              fw= new BufferedWriter( new OutputStreamWriter(new FileOutputStream(indexFile),"UTF-8"));//以UTF-8的格式写⼊⽂件
28              bw=new PrintWriter(fw);
29              bw.String());
30        }catch(Exception e){
31throw new String());//错误扔出
32        }finally{
33if (bw != null) {
34                bw.close();
35            }
36if(fw!=null){
37                fw.close();
38            }
39        }
40        String zipFilePath=String.format("%s%s%s.%s", uploadPath,File.separator,fileName,"ZIP");
41        scormService.zip(basePath, zipFilePath);
42//删除⽂件
43        file.delete();
44        FileUtils.forceDelete(new File(basePath));
45return new ProcessFileInfo(true,new File(zipFilePath).getName(),zipFilePath);
46    }
View Code
  虽然需求最终还是改成最简单的实现⽅式,这中间近乎⽩忙活的结果研究出来的实现⽅案还是有必要分享的,以上如能帮助到开发者,哪怕只有⼀位,也是⾮常值得的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。