之前接到了一个任务, 把 jsp 中的 table 转成一个图片, 保存在指定文件夹并显示在前端.
我的思路是: 一, 引用第三方 js 在前端把 table 转成图片
二, 通过 ajax 把图片上传到服务器, 保存在指定文件夹
三, 浏览器根据文件名从服务器端获取图片
一, 引用第三方 js 在前端把 table 转成图片
一开始我在百度找到了比较多人用过的 https://github.com/niklasvh/html2canvas , 据说很多坑, 但由于这些坑都是几年前被发现的, 我觉得现在更新了这么多个版本应该没啥问题了吧. 考虑到稳定性, 我下载了 0.4.1 版本, 还真的有坑, 只能把可视区域内的 html 给转换出来, 毕竟我的表格数据多变, 这种效果肯定是不行的.
经过了一轮的百度, 我从一位大神的贴子中找到了解决方法, 需要 0.5.0 版本, 使用 html2canvas 实现浏览器截图. 解决方法是修改一小段源码, 通过设置截图区域的 width 和 height 来截取内容, 于是我把 width 和 height 分别附上 table 的 div 的宽和高, 出来的效果是 -- 还是差一点, 虽然能突破了只能在可视区域截取内容的障碍, 但是再截图区域的宽高设置上还得手动给它加个几十像素去让它截取完整, 这样肯定会出 bug.
一番折腾后, 我放弃了这个插件了, 不好用. 转战谷歌, 看看有啥更好地第三方插件
功夫不负有心人, 它就是 -- https://github.com/tsayen/dom-to-image
dom-to-image 介绍
这是一个与 html2canvas 功能差不多的第三方 js 插件, 能够把 dom 节点转换为矢量图 (svg) 和位图(png 和 jpeg), 完美解决了 html2canvas 出现过的坑.
使用的代码如下(转成 png):
- var node = document.getElementById('table');
- domtoimage.toPng(node)
- .then(function (dataUrl) {
- var img = new Image();
- img.src = dataUrl;
- document.body.appendChild(img);
- });
无论我的表格有多大, 它都能全部获取到, 图片稍微失真.
二, 通过 ajax 把图片上传到服务器, 保存在指定文件夹
我发现 dom-to-image 返回的 png 图片是通过 Base64 编码的, 表现的方式基本如下:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAsZCykDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/
需要在后台进行解码才能保存为文件(需要注意的是, 把 "data:image/jpeg;base64" 去掉再进行解码, 否则生成的文件会提示已损坏)
- /**
- * 转换 url:data 数据为正常图片
- * @param dataUrl Base64 编码的图片
- * @return 返回文件名
- */
- public String getDataUrlPic(String dataUrl){
- String ID = RandomGUID.getGUID();
- String imgName = "table-" + ID + ".png";
- String imgPath = getImgPath();
- if(GenerateImage(dataUrl,imgName,imgPath)){
- return imgName;
- }
- return "";
- }
- /**
- * 把转换后的图片存放到指定目录
- * @param imgStr dataUrl
- * @param imgName 图片名称
- * @param imgPath 存放路径
- * @return
- */
- public boolean generateImage(String imgStr,String imgName,String imgPath){
- // 把 "data:image/jpeg;base64" 去掉,
- imgStr = imgStr.substring(imgStr.indexOf(",") + 1);
- if (imgStr == null) {
- return false;
- }
- BASE64Decoder decoder = new BASE64Decoder();
- try {
- // Base64 解码
- byte[] b = decoder.decodeBuffer(imgStr);
- for (int i = 0; i <b.length; ++i) {
- if (b[i] < 0) {// 调整异常数据
- b[i] += 256;
- }
- }
- File headPath = new File(imgPath);
- if (!headPath.exists()) {
- headPath.mkdirs();
- }
- String imgFilePath = imgPath + "/" + imgName;
- OutputStream out = new FileOutputStream(imgFilePath);
- out.write(b);
- out.flush();
- out.close();
- return true;
- } catch (Exception e) {
- return false;
- }
- }
但是问题来了, 当我的表格数据多的时候, 发现导出来的图片已损坏. 原因是字符串过长提交失败, 网上的说法也不一致, 有的说 post 限制 2m 的提交, 要更改服务器配置(本人用的 tomcat); 也有说 post 无限制, 无需修改. 修改配置的方法我试过, 没效果, 无需修改? 明明不行啊......
经过多次的尝试, 我发现转成 Blob 图片后使用 ajax 传输到后台并不会出现上述问题. 而且用原生的 ajax 并非 jquery 封装过的 ajax, 代码如下:
- var node = document.getElementById('table');
- var responseText;
- domtoimage.toBlob(node)
- .then(function (blob) {
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/test', true);
- xhr.onreadystatechange = function(){
- if(xhr.readyState == 4 && xhr.status == 200){
- responseText = xhr.responseText;
- if(responseText != ""){
- // 拼 servlet 地址放入 img 标签的 src 属性中
- var reportUrl = "/EditorChartServlet?filename=" + responseText;
- $("img").attr("src",reportUrl);
- }
- }
- };
- xhr.setRequestHeader("Content-Type", "image/png");
- xhr.send(blob);
- });
所以后台无需进行解码, 而是在 ajax 里的 url 所请求的 servlet 中把 Blob 图片转存到指定文件夹中即可, servlet 的代码如下:
- package ctx.ajax;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.webServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- @WebServlet(name = "TestUpload", urlPatterns = "/test")
- public class TestUpload extends HttpServlet {
- private static final long serialVersionUID = 1L;
- protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- String imgName = "table-test.png";
- String imgPath = MediaUtil.getImgPath();
- String imgFilePath = imgPath + "/" + imgName;
- byte[] buffer = new byte[1024 * 1024];
- InputStream input = request.getInputStream();
- OutputStream output = new FileOutputStream(imgFilePath);
- int bytesRead;
- while ((bytesRead = input.read(buffer)) != -1){
- // System.out.println(bytesRead);
- output.write(buffer, 0, bytesRead);
- }
- output.close();
- input.close();
- response.getOutputStream().print(imgName);
- }
- }
三, 浏览器根据文件名从服务器端获取图片
Servlet 的代码如下:
- package ctx.servlet;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;public class EditorChartServlet extends HttpServlet{
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- String filename = request.getParameter("filename");if (filename == null) {
- throw new ServletException("Parameter'filename'must be supplied");
- }
- filename = ServletUtilities.searchReplace(filename, "..", "");
- String imgPath = MediaUtil.getImgPath();
- File file = new File(imgPath, filename);
- if (!(file.exists())) {
- throw new ServletException("File'" + file.getAbsolutePath() + "'does not exist");
- }
- ServletUtilities.sendTempFile(file, response);
- }
- }
所用到的 ServletUtil 方法代码如下:
- public static String searchReplace(String inputString, String searchString, String replaceString) {
- int i = inputString.indexOf(searchString);
- if (i == -1) {
- return inputString;
- }
- String r = "";
- r = r + inputString.substring(0, i) + replaceString;
- if (i + searchString.length() < inputString.length()) {
- r = r + searchReplace(inputString.substring(i + searchString.length()), searchString, replaceString);
- }
- return r;
- }
- public static void sendTempFile(File file, HttpServletResponse response) throws IOException {
- String mimeType = null;
- String filename = file.getName();
- if (filename.length()> 5) {
- if (filename.substring(filename.length() - 5, filename.length()).equals(".jpeg")) {
- mimeType = "image/jpeg";
- } else if (filename.substring(filename.length() - 4, filename.length()).equals(".png")) {
- mimeType = "image/png";
- }
- }
- sendTempFile(file, response, mimeType);
- }
- public static void sendTempFile(File file, HttpServletResponse response, String mimeType) throws IOException {
- if (file.exists()) {
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
- if (mimeType != null) {
- response.setHeader("Content-Type", mimeType);
- }
- response.setHeader("Content-Length", String.valueOf(file.length()));
- SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
- sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
- response.setHeader("Last-Modified", sdf.format(new Date(file.lastModified())));
- BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
- byte[] input = new byte[1024];
- boolean eof = false;
- while (!(eof)) {
- int length = bis.read(input);
- if (length == -1) {
- eof = true;
- } else {
- bos.write(input, 0, length);
- }
- }
- bos.flush();
- bis.close();
- bos.close();
- } else {
- throw new FileNotFoundException(file.getAbsolutePath());
- }
- }
希望对大家有帮助, 多多交流.
来源: https://www.cnblogs.com/CTXXCH/p/6721893.html