使用POI将Word转为HTML时关于图片的处理
需求
之前发过一篇博客记录使用POI将Word文档转为HTML文件的方法. 最近在将那个方法整合为项目中能用的接口,但在实际操作中发现了一些问题. 由于我的需求是将试卷上传,将其内容转为HTML后按照题号进行分割再存入数据库,所以对于试题中的图片部分,要像之前项目中一样上传到我们的云服务器上,图片的src里也是一个url. 这样一来,必须要实现对img中的src替换,将网上其他例子中的通过File对象获取的路径替换为一个url.
实现
doc文档 先把全部代码贴上来
public File docToHtml(File docFile) { String htmlPath=docFile.getAbsolutePath().replaceAll(docFile.getName(),"")+docFile.getName().replaceAll(".doc",".html"); OutputStreamWriter outputStreamWriter = null; if(!docFile.exists()){ System.out.println("文件不存在"); }else try { //加载word文档生成XWPF对象 InputStream in = new FileInputStream(docFile); HWPFDocument doc=new HWPFDocument(in); org.w3c.dom.Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); WordToHtmlConverter wordToHtmlConverter=new WordToHtmlConverter(document); //保存图片,并返回图片的相对路径 //将图片上传,调用上传的方法 wordToHtmlConverter.setPicturesManager((content, pictureType, name, width, height) -> { Map<String, String> gatewayConfig = fileConfig.getGatewayConfig(fileConfig.getGateway()); String bucketName = gatewayConfig.get("bucketName"); String imgName=System.currentTimeMillis() + "_"+name; try { String hash = qiniuFileManager.uploadali(content,imgName, bucketName);//bucketName参数无用 } catch (IOException e) { e.printStackTrace(); } return gatewayConfig.get("alilink") + "/" + imgName; }); wordToHtmlConverter.processDocument(doc); org.w3c.dom.Document htmlDocument = wordToHtmlConverter.getDocument(); DOMSource domSource = new DOMSource(htmlDocument); StreamResult streamResult = new StreamResult(new File(htmlPath)); TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty(OutputKeys.METHOD, "html"); serializer.transform(domSource, streamResult); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } return new File(htmlPath); }
对图片src的处理是wordToHtmlConverter的setPicturesManager方法,该方法的参数是
public void setPicturesManager(PicturesManager fileManager) { this.picturesManager = fileManager; }
一个PicturesManager接口,于是对该接口中的
String savePicture(byte[] var1, PictureType var2, String var3, float var4, float var5);
这个方法进行重写即可实现对src的修改,该接口中的方法的参数分别是:
byte[] var1, PictureType var2, String var3, float var4, float var5 图片byte,图片type,图片名,图片宽度,图片高度
所以在实现过程中,我将图片上传到云服务器和获取图片url的方法写在了这里
wordToHtmlConverter.setPicturesManager((content, pictureType, name, width, height) -> { //获取上传配置 Map<String, String> gatewayConfig = fileConfig.getGatewayConfig(fileConfig.getGateway()); String bucketName = gatewayConfig.get("bucketName"); String imgName=System.currentTimeMillis() + "_"+name;//设置图片文件名 try { //上传图片 String hash = qiniuFileManager.uploadali(content,imgName, bucketName);//bucketName参数无用 } catch (IOException e) { e.printStackTrace(); } return gatewayConfig.get("alilink") + "/" + imgName;//获取图片url });
这样重写完成之后,调用该方法,生成的html文件中图片的src就是上传之后的url了 效果:
docx文档 对于docx文档的处理废了一些功夫,因为不像doc一样对于图片的处理的类是直接在converter里面的,而是在XHTMLOptions中的,对于图片的处理则是封装在options.setExtractor中的,所以找到可以重写的接口废了一段时间,最后还是论坛的朋友提供了方法. 这里贴一下帖子地址: 之前我考虑去写一个类继承ImageManager,但是论坛的朋友提出重写IURIResolver接口,实测可以. 源代码:
@Override public File docxToHtml(File docxFile) { String htmlPath=docxFile.getAbsolutePath().replaceAll(docxFile.getName(),"")+docxFile.getName().replaceAll(".docx",".html"); String imagePath=docxFile.getAbsolutePath().replaceAll(docxFile.getName(),"")+"docxImage\"; File imageFile=new File(imagePath); if(!docxFile.exists()){ System.out.println("文件不存在!"); } Map<String, String> gatewayConfig = fileConfig.getGatewayConfig(fileConfig.getGateway()); String bucketName = gatewayConfig.get("bucketName"); try { InputStream in=new FileInputStream(docxFile); XWPFDocument document=new XWPFDocument(in); //存储图片 XHTMLOptions options=XHTMLOptions.create().URIResolver(new FileURIResolver(imageFile)); options.setExtractor(new FileImageExtractor(imageFile)); options.URIResolver((uri)->{ File imgFile=new File(imagePath+uri);//获取图片 String imgName=System.currentTimeMillis() + "_"+imgFile.getName();//设置图片文件名 InputStream is= null; try { is = new FileInputStream(imgFile); byte[] isbyte=new byte[is.available()]; is.read(isbyte); //上传图片 String hash = qiniuFileManager.uploadali(isbyte,imgName, bucketName);//bucketName参数无用 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return gatewayConfig.get("alilink") + "/" + imgName;//返回图片url }); OutputStream out=new FileOutputStream(htmlPath); XHTMLConverter converter=new XHTMLConverter(); converter.convert(document,out,options); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return new File(htmlPath); }
大体上传过程与doc的类似 docx的方法是在options.URIResolver这个方法中,这个方法的参数是
public XHTMLOptions URIResolver(IURIResolver resolver) { this.resolver = resolver; return this; }
IURIResolver接口的resolve方法即根据参数uri返回uri,所以重写这个方法即可.
public interface IURIResolver { String resolve(String var1); }
所以我按照上方贴的源代码重写了这个方法,
options.URIResolver((uri)->{ File imgFile=new File(imagePath+uri); String imgName=System.currentTimeMillis() + "_"+imgFile.getName(); InputStream is= null; try { is = new FileInputStream(imgFile); byte[] isbyte=new byte[is.available()]; is.read(isbyte); String hash = qiniuFileManager.uploadali(isbyte,imgName, bucketName);//bucketName参数无用 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return gatewayConfig.get("alilink") + "/" + imgName; });
参数uri是图片在本地缓存的相对路径,根据路径得到图片文件,然后将File转为byte[],调用上传接口,最后返回上传之后的url.就能达到和doc同样的效果,这里就不再贴图了
再次感谢论坛回复我的那位朋友!