昨天写了下文件转PDF简易运用的短例,有人问我,他是前后端分离的,用流形式转PDF怎么转。其实这个对于图片来说比较简单,对于word、excel来说比较复杂一点,顺便补充下excel转PDF。
本次讲解的案例是:前端我用thymeleaf搭配layui控件,用layui.upload的方式上传本地文件。
简短补充下layui的使用,这个控件还是蛮好用的。
首先去它的官网:https://www.layui.com/ 下载整个包
下载完后把他放到static目录下
接下来就是html中引用了,只需要引用一个css、一个js
th:href="@{/layui/css/layui.css}" rel="stylesheet">
上传文件的接口方法,该方法写入js中
layui.use('upload',function(){const $ = layui.jquery , upload = layui.upload; //拖拽上传 upload.render({elem: '#test10' ,url: basePath +'/fileChange.json' //改成您自己的上传接口 ,accept: "file" //,size: 1048576 ,progress: function (e, percent) {console.log(JSON.stringify(e)); console.log("进度:" + e + '%'); },done: function(res){const code = res.code; const msg = res.msg; if(code === "0000") {
layer.msg('文件转PDF成功'); } else {
layer.msg(msg); }//layui.$('#uploadDemoView').removeClass('layui-hide').find('img').attr('src', res.files.file); console.log(res)
}
});});
后端如何接收解析前端传过来的文件信息呢,我这里用的方式是解析request的信息,
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;// 获取上传的文件MultipartFile multiFile = multipartRequest.getFile("file");
文件流怎么获取呢:multiFile.getInputStream()
好了我们来对比一下图片流形式转PDF和图片路径转PDF的区别,看代码
/** * 图片转PDF(文件流形式) * @param multiFile * @param pdfOutPath * @return */private boolean pictureToPDF(MultipartFile multiFile, String pdfOutPath) {boolean changeResult = false; Document doc = null; try {
FileOutputStream fos = new FileOutputStream(pdfOutPath); doc = new Document(null, 0, 0, 0, 0); // 写入PDF文档 PdfWriter.getInstance(doc, fos); // 读取图片流 BufferedImage img = null; // 实例化图片 Image image = null; float newHeight = 0f, newWidth = 0f; img = ImageIO.read(multiFile.getInputStream()); Map,Float> result = setImageWidthHeight(img.getWidth(), img.getHeight(),newWidth, newHeight); newWidth = result.get("width"); newHeight = result.get("height"); doc.setPageSize(new Rectangle(newWidth,newHeight)); image = Image.getInstance(multiFile.getBytes()); image.scaleAbsolute(new Rectangle(newWidth,newHeight)); // 添加图片到文档 doc.newPage(); doc.open(); doc.add(image); // 关闭文档 doc.close(); changeResult = true; System.out.println("图片转PDF成功"); } catch (Exception e) {
System.out.println("图片转PDF失败"); e.printStackTrace(); } finally {if(doc != null) doc.close(); }return changeResult;}
/** * 图片转PDF(文件路径形式) * @param imagPath * @param pdfOutPath * @return */private boolean pictureToPDF(String imagPath, String pdfOutPath) {boolean changeResult = false; try {
FileOutputStream fos = new FileOutputStream(pdfOutPath); Document doc = new Document(null, 0, 0, 0, 0); // 写入PDF文档 PdfWriter.getInstance(doc, fos); // 读取图片流 BufferedImage img = null; // 实例化图片 Image image = null; float newHeight = 0f, newWidth = 0f; img = ImageIO.read(new File(imagPath)); Map,Float> result = setImageWidthHeight(img.getWidth(), img.getHeight(),newWidth, newHeight); newWidth = result.get("width"); newHeight = result.get("height"); doc.setPageSize(new Rectangle(newWidth,newHeight)); image = Image.getInstance(imagPath); image.scaleAbsolute(new Rectangle(newWidth,newHeight)); // 添加图片到文档 doc.newPage(); doc.open(); doc.add(image); // 关闭文档 doc.close(); changeResult = true; System.out.println("图片转PDF成功"); } catch (Exception e) {
System.out.println("图片转PDF失败"); e.printStackTrace(); }return changeResult;}
两者区别不大,通过图片截图来更好的看一下,就是读取图片的时候一个是通过路径形式,一个是通过流形式;这是因为方法同时支持路径和流形式。
word和excel的方法目前就没有发现可以同时支持路径和流的,所以还是用老方法用路径比较合适。那么你可能会问,前后端分离怎么传文件路径过来,传过来的路径也解析不到呀。别急。听我慢慢道来
有好几种方法可以解决:
- 前端传输文件,后端request解析,解析出文件流同时转成文件报错到服务器磁盘(保存的时候可以的到文件路径)
- 讲文件传到通用的存储控件,比如说OSS服务器(这个要花钱买,鉴于小型开发者可能没有这个需求和必要,可以暂不考虑)
我是一般用第一种
//将文件保存到当前服务器磁盘InputStream inputStream = multiFile.getInputStream();String totalPath = fileSavePath + fileName;FileUtils.createFolder(fileSavePath);boolean result = FileUtils.saveFileByStream(inputStream, totalPath);
package jp.utils;import java.io.File;import java.io.FileOutputStream;import java.io.InputStream;/** * 文件处理工具类 */public class FileUtils {public static String createFolder(String path,String folderName) {
File folder = new File(path+File.separator+folderName); if(!folder.exists()) {
folder.mkdirs(); }return folder.getAbsolutePath(); }public static void createFolder(String path) {
File folder = new File(path); if(!folder.exists()) {
folder.mkdirs(); }
}/** * 获取文件后缀 * @param fileName * @return */ public static String getFileSuffix(String fileName) {int splitIndex = fileName.lastIndexOf("."); return fileName.substring(splitIndex + 1); }public static String getFileNameNoEx(String filename) {if ((filename != null) && (filename.length() > 0)) {int dot = filename.lastIndexOf('.'); if ((dot >-1) && (dot < (filename.length()))) {return filename.substring(0, dot); }
}return filename; }/** * 文件流保存到服务磁盘 * @param inputStream * @param filePath * @return */ public static boolean saveFileByStream(InputStream inputStream, String filePath) {boolean result = false; FileOutputStream fos = null; try {
fos = new FileOutputStream(filePath); byte buffer[] = new byte[inputStream.available()]; int length = 0; while((length = inputStream.read(buffer))>0){
fos.write(buffer, 0, length); }//关闭输入流 inputStream.close(); fos.close(); result = true; } catch (Exception e) {
e.printStackTrace(); } finally {try {if(fos != null) {
fos.close(); }if(inputStream !=null) {
inputStream.close(); }
} catch (Exception e2) {
}
}return result; }
}
下面废话不多少,我们看一下实际效果,最后我会贴上全部代码
点击文件上传或者拖拽,选择D盘的图片,(后端默认存到D:/root/deploy)
执行完,看一下效果
接下来演示excel
Excel写两页
是不是Excel也成功执行了。本来我也想搞一个ppt的,但是失败了,网上也没有找到解决的资料,只能放弃了,下次如果找到解决的话,我会补充的。
最后奉上完整的代码:此代码跟上一篇中的代码相比做了一些优化
前端html代码:
/span>html>lang="en" xmlns:th="http://www.thymeleaf.org"> charset="UTF-8"> name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> th:href="@{/bootstrap/bootstrap-3.3.7/css/bootstrap.min.css}" rel="stylesheet" type="text/css" > th:href="@{/layui/css/layui.css}" rel="stylesheet"> :inline="text">
table{border-spacing: 10px 30px;}
tr {
display: block;
/*将tr设置为块体元素*/
margin: 3px 0;
/*设置tr间距为2px*/
}
td {
text-align: right;
}
.layui-tab-content {
height: 200vh;
}
style="margin-top:3%;margin-left:1%;margin-right:2%;height: 250px;">
style="width: 33%; float: left">
class="layui-form-item" > class="layui-elem-field layui-field-title" style="margin-top: 30px;"> 文件转PDF
class="layui-upload-drag" id="test10"> class="layui-icon">
class="layui-hide" id="uploadDemoView"> src="" alt="上传成功后渲染" style="max-width: 196px">
controller
package jp.controller.business;import jp.service.IFileHandleService;import jp.vo.ResultVo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@Controllerpublic class fileController {@Autowired IFileHandleService fileHandleService; /** * 图片转换 * @param request * @return */ @RequestMapping(value = "/fileChange.json", produces = "application/json;charset=UTF-8", method = RequestMethod.POST)@ResponseBody public ResultVo fileChangeMethod(HttpServletRequest request) {return fileHandleService.fileToPdf(request); }
}
service
package jp.service.impl;import com.itextpdf.text.Document;import com.itextpdf.text.Image;import com.itextpdf.text.Rectangle;import com.itextpdf.text.pdf.PdfWriter;import com.jacob.activeX.ActiveXComponent;import com.jacob.com.ComThread;import com.jacob.com.Dispatch;import com.jacob.com.Variant;import jp.service.IFileHandleService;import jp.utils.FileUtils;import jp.utils.PDFUtils;import jp.utils.ResultVoUtil;import jp.vo.ResultVo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.env.Environment;import org.springframework.stereotype.Service;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.multipart.MultipartHttpServletRequest;import javax.imageio.ImageIO;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.awt.image.BufferedImage;import java.io.*;import java.util.HashMap;import java.util.Map;@Servicepublic class FileHandleImpl implements IFileHandleService {private static final int regular_width = 842; private static final int regular_height = 595; private static final int vertical_regular_width = 595; private static final int vertical_regular_height = 842; static final int WORD_FORMAT_PDF = 17; static final int PPT_FORMAT_PDF = 32; static final int EXCEL_FORMAT_PDF = 0; @Autowired Environment env; @Override public ResultVo fileToPdf(HttpServletRequest request) {//yml中配置的文件保存路径(D:/root/deploy/) String fileSavePath = env.getProperty("manager.save-path"); try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; // 获取上传的文件 MultipartFile multiFile = multipartRequest.getFile("file"); assert multiFile != null; String fileName = multiFile.getOriginalFilename(); String suffix = FileUtils.getFileSuffix(fileName); String fileNameNoExt = FileUtils.getFileNameNoEx(fileName); String contentType = multiFile.getContentType(); String pdfOutPath = fileSavePath + fileNameNoExt + ".pdf"; if(contentType.contains("image/")) {boolean result = pictureToPDF(multiFile, pdfOutPath); if(result) return ResultVoUtil.success(); } else {//将文件保存到当前服务启磁盘 InputStream inputStream = multiFile.getInputStream(); String totalPath = fileSavePath + fileName; FileUtils.createFolder(fileSavePath); boolean result = FileUtils.saveFileByStream(inputStream, totalPath); if(!result) return ResultVoUtil.error("E111","文件保存到磁盘失败"); boolean changSuc = false; if (suffix.equals("doc") || suffix.equals("docx") || suffix.equals("txt")) {
changSuc = wordToPDF(totalPath, pdfOutPath); } else if (suffix.equals("ppt") || suffix.equals("pptx")) {
changSuc = pptToPDF(totalPath, pdfOutPath); } else if (suffix.equals("xls") || suffix.equals("xlsx")) {
changSuc = excelToPDF(totalPath, pdfOutPath); } else {return ResultVoUtil.error("W111", "暂不支持该文件格式("+suffix+")转PDF"); }if(changSuc) return ResultVoUtil.success(); }
} catch (Exception e) {
e.printStackTrace(); }return ResultVoUtil.error("0001", "转换失败"); }/** * 文件填充 * @param request * @param response * @return */ @Override public ResultVo fileFill(HttpServletRequest request, HttpServletResponse response) {
String fileSavePath = env.getProperty("manager.save-path"); OutputStream os = null; InputStream inputStream = null; try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; // 获取上传的文件 MultipartFile multiFile = multipartRequest.getFile("file"); assert multiFile != null; String fileName = multiFile.getOriginalFilename(); String pdfOutPath = fileSavePath + fileName; inputStream = multiFile.getInputStream();// //这是直接输出到磁盘// FileOutputStream outputStream = new FileOutputStream(pdfOutPath); //直接输出 byte[] byt = new byte[inputStream.available()]; inputStream.read(byt); Map, String> data = new HashMap<>(); data.put("username", "小明"); data.put("sex", "男"); data.put("like", "女"); //通过浏览器输出 response.reset(); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("gbk"), "iso8859-1")); os = response.getOutputStream(); boolean result = PDFUtils.fillTemplate(os, byt, data); System.out.println("填充成功"); response.getOutputStream().flush(); response.getOutputStream().close(); os.close(); inputStream.close(); if(result) return ResultVoUtil.success(); } catch (Exception e) {
e.printStackTrace(); } finally {if(inputStream != null) {try {
inputStream.close(); if(os != null)os.close(); } catch (IOException e) {
e.printStackTrace(); }
}
}return ResultVoUtil.error("0001", "PDF填充失败"); }/** * 图片转PDF(文件流形式) * @param multiFile * @param pdfOutPath * @return */ private boolean pictureToPDF(MultipartFile multiFile, String pdfOutPath) {boolean changeResult = false; Document doc = null; try {
FileOutputStream fos = new FileOutputStream(pdfOutPath); doc = new Document(null, 0, 0, 0, 0); // 写入PDF文档 PdfWriter.getInstance(doc, fos); // 读取图片流 BufferedImage img = null; // 实例化图片 Image image = null; float newHeight = 0f, newWidth = 0f; img = ImageIO.read(multiFile.getInputStream()); Map,Float> result = setImageWidthHeight(img.getWidth(), img.getHeight(),newWidth, newHeight); newWidth = result.get("width"); newHeight = result.get("height"); doc.setPageSize(new Rectangle(newWidth,newHeight)); image = Image.getInstance(multiFile.getBytes()); image.scaleAbsolute(new Rectangle(newWidth,newHeight)); // 添加图片到文档 doc.newPage(); doc.open(); doc.add(image); // 关闭文档 doc.close(); changeResult = true; System.out.println("图片转PDF成功"); } catch (Exception e) {
System.out.println("图片转PDF失败"); e.printStackTrace(); } finally {if(doc != null) doc.close(); }return changeResult; }/** * 图片转PDF(文件路径形式) * @param imagPath * @param pdfOutPath * @return */ private boolean pictureToPDF(String imagPath, String pdfOutPath) {boolean changeResult = false; try {
FileOutputStream fos = new FileOutputStream(pdfOutPath); Document doc = new Document(null, 0, 0, 0, 0); // 写入PDF文档 PdfWriter.getInstance(doc, fos); // 读取图片流 BufferedImage img = null; // 实例化图片 Image image = null; float newHeight = 0f, newWidth = 0f; img = ImageIO.read(new File(imagPath)); Map,Float> result = setImageWidthHeight(img.getWidth(), img.getHeight(),newWidth, newHeight); newWidth = result.get("width"); newHeight = result.get("height"); doc.setPageSize(new Rectangle(newWidth,newHeight)); image = Image.getInstance(imagPath); image.scaleAbsolute(new Rectangle(newWidth,newHeight)); // 添加图片到文档 doc.newPage(); doc.open(); doc.add(image); // 关闭文档 doc.close(); changeResult = true; System.out.println("图片转PDF成功"); } catch (Exception e) {
System.out.println("图片转PDF失败"); e.printStackTrace(); }return changeResult; }/** * word转PDF * @param docPath * @param pdfOutPath * @return */ private boolean wordToPDF(String docPath, String pdfOutPath) {
Boolean changeResult = false; ActiveXComponent app = null; try{//打开word应用程序 app = new ActiveXComponent("Word.Application"); //设置word不可见 app.setProperty("Visible", false); //获得word中所有打开的文档,返回Documents对象 Dispatch docs = app.getProperty("Documents").toDispatch(); //调用Documents对象中Open方法打开文档,并返回打开的文档对象Document Dispatch doc = Dispatch.call(docs, "Open", docPath, false, true ).toDispatch(); //调用Document对象的SaveAs方法,将文档保存为pdf格式 Dispatch.call(doc, "ExportAsFixedFormat", pdfOutPath, WORD_FORMAT_PDF //word保存为pdf格式宏,值为17 ); //关闭文档 Dispatch.call(doc, "Close",false); System.out.println("doc文档转PDF成功"); changeResult = true; } catch (Exception e) {
System.out.println("doc文档转PDF失败"); e.printStackTrace(); } finally {if (app != null) {//关闭word应用程序 app.invoke("Quit", new Variant[0]); }
}//如果没有这句话,winword.exe进程将不会关闭 ComThread.Release(); return changeResult; }/** * Excel转PDF * @param inputFile * @param pdfOutPath */ private boolean excelToPDF(String inputFile, String pdfOutPath) {boolean changeResult = false; System.out.println("启动Excel..."); long start = System.currentTimeMillis(); ActiveXComponent app = null; Dispatch excel = null; try {// 创建一个excel对象 app = new ActiveXComponent("Excel.Application"); // 不可见打开excel app.setProperty("Visible", new Variant(false)); // 获取文挡属性 Dispatch excels = app.getProperty("Workbooks").toDispatch(); // 调用Documents对象中Open方法打开文档,并返回打开的文档对象Document excel = Dispatch.call(excels, "Open", inputFile).toDispatch(); System.out.println("打开文档..." + inputFile); System.out.println("转换文档到PDF..." + pdfOutPath); File toFile = new File(pdfOutPath); if (toFile.exists()) {
toFile.delete(); }// Excel不能调用SaveAs方法 Dispatch.call(excel, "ExportAsFixedFormat", EXCEL_FORMAT_PDF, pdfOutPath); long end = System.currentTimeMillis(); System.out.println("转换完成..用时:" + (end - start) + "ms."); changeResult = true; } catch (Exception e) {
System.out.println("========Error:文档转换失败:" + e.getMessage()); } finally {
Dispatch.call(excel, "Close", false); System.out.println("关闭文档"); if (app != null)
app.invoke("Quit", new Variant[]{}); }//如果没有这句话,winword.exe进程将不会关闭 ComThread.Release(); return changeResult; }/** * ppt转PDF * @param inputFile * @param pdfFile * @return */ private boolean pptToPDF(String inputFile, String pdfFile) {boolean changeResult = false; System.out.println("启动PPT..."); long start = System.currentTimeMillis(); ActiveXComponent app = null; Dispatch ppt = null; try {// 创建一个ppt对象 app = new ActiveXComponent("PowerPoint.Application"); // 不可见打开(PPT转换不运行隐藏,所以这里要注释掉) // app.setProperty("Visible", new Variant(false)); // 获取文挡属性 Dispatch ppts = app.getProperty("Presentations").toDispatch(); // 调用Documents对象中Open方法打开文档,并返回打开的文档对象Document ppt = Dispatch.call(ppts, "Open", inputFile, true, true, false).toDispatch(); System.out.println("打开文档..." + inputFile); System.out.println("转换文档到PDF..." + pdfFile); File tofile = new File(pdfFile); if(tofile.exists()) {
tofile.delete(); }
Dispatch.call(ppt, "SaveAs", pdfFile, PPT_FORMAT_PDF); long end = System.currentTimeMillis(); System.out.println("转换完成..用时:" + (end - start) + "ms."); changeResult = true; } catch (Exception e) {
System.out.println("========Error:文档转换失败:" + e.getMessage()); } finally {
Dispatch.call(ppt, "Close"); System.out.println("关闭文档"); if (app != null)
app.invoke("Quit", new Variant[] {}); }//如果没有这句话,winword.exe进程将不会关闭 ComThread.Release(); return changeResult; }/** * 设置PDF的宽高 * * @param width * @param height * @param newWidth * @param newHeight * @return */ private Map, Float> setImageWidthHeight(float width, float height, float newWidth, float newHeight) {float res = width / height; if (res >= 1) { //横向 if (width > regular_width) {
newWidth = regular_width; newHeight = (regular_width / width) * height; } else if (height > regular_height) {
newHeight = regular_height; newWidth = (regular_height / height) * width; } else {//如果都在范围内,不做任何操作 newWidth = width; newHeight = height; }
} else {if (width > vertical_regular_width) {
newWidth = vertical_regular_width; newHeight = (vertical_regular_width / width) * height; } else if (height > vertical_regular_height) {
newHeight = vertical_regular_height; newWidth = (vertical_regular_height / height) * width; } else {//如果都在范围内,不做任何操作 newWidth = width; newHeight = height; }
}
Map, Float> result = new HashMap, Float>(); result.put("width", newWidth); result.put("height", newHeight); return result; }
}
yml配置截图哈
以上就是整个讲解和内容补充了。
提前告诉你一下:明天不发文!!!