之前接到了一个任务,把jsp中的table转成一个图片,保存在指定文件夹并显示在前端。

  我的思路是:一、引用第三方js在前端把table转成图片

        二、通过ajax把图片上传到服务器,保存在指定文件夹

        三、浏览器根据文件名从服务器端获取图片

  

  一、引用第三方js在前端把table转成图片

  一开始我在百度找到了比较多人用过的html2canvas,据说很多坑,但由于这些坑都是几年前被发现的,我觉得现在更新了这么多个版本应该没啥问题了吧。考虑到稳定性,我下载了0.4.1版本,还真的有坑,只能把可视区域内的html给转换出来,毕竟我的表格数据多变,这种效果肯定是不行的。

  经过了一轮的百度,我从一位大神的贴子中找到了解决方法,需要0.5.0版本,使用html2canvas实现浏览器截图。解决方法是修改一小段源码,通过设置截图区域的width和height来截取内容,于是我把width和height分别附上table的div的宽和高,出来的效果是——还是差一点,虽然能突破了只能在可视区域截取内容的障碍,但是再截图区域的宽高设置上还得手动给它加个几十像素去让它截取完整,这样肯定会出bug。

  一番折腾后,我放弃了这个插件了,不好用。转战谷歌,看看有啥更好地第三方插件

  功夫不负有心人,它就是——dom-to-image

  dom-to-image介绍

  这是一个与html2canvas功能差不多的第三方js插件,能够把dom节点转换为矢量图(svg)和位图(png和jpeg),完美解决了html2canvas出现过的坑。

  使用的代码如下(转成png):

  

  1. var node = document.getElementById('table');
  2.  
  3. domtoimage.toPng(node)
  4. .then(function (dataUrl) {
  5. var img = new Image();
  6. img.src = dataUrl;
  7. document.body.appendChild(img);
  8. });

  无论我的表格有多大,它都能全部获取到,图片稍微失真。

  二、通过ajax把图片上传到服务器,保存在指定文件夹

  我发现dom-to-image返回的png图片是通过Base64编码的,表现的方式基本如下:

  1. data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAsZCykDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/

  需要在后台进行解码才能保存为文件(需要注意的是,把“data:image/jpeg;base64”去掉再进行解码,否则生成的文件会提示已损坏)

  1. /**
  2. * 转换url:data数据为正常图片
  3. * @param dataUrl Base64编码的图片
  4. * @return 返回文件名
  5. */
  6. public String getDataUrlPic(String dataUrl){
  7. String ID = RandomGUID.getGUID();
  8. String imgName = "table-" + ID + ".png";
  9. String imgPath = getImgPath();
  10. if(GenerateImage(dataUrl,imgName,imgPath)){
  11. return imgName;
  12. }
  13. return "";
  14. }
  15.  
  16. /**
  17. * 把转换后的图片存放到指定目录
  18. * @param imgStr dataUrl
  19. * @param imgName 图片名称
  20. * @param imgPath 存放路径
  21. * @return
  22. */
  23. public boolean generateImage(String imgStr,String imgName,String imgPath){
  24. //把“data:image/jpeg;base64”去掉,
  25. imgStr = imgStr.substring(imgStr.indexOf(",") + 1);
  26. if (imgStr == null) {
  27. return false;
  28. }
  29. BASE64Decoder decoder = new BASE64Decoder();
  30. try {
  31. // Base64解码
  32. byte[] b = decoder.decodeBuffer(imgStr);
  33. for (int i = 0; i < b.length; ++i) {
  34. if (b[i] < 0) {// 调整异常数据
  35. b[i] += 256;
  36. }
  37. }
  38. File headPath = new File(imgPath);
  39. if (!headPath.exists()) {
  40. headPath.mkdirs();
  41. }
  42. String imgFilePath = imgPath + "/" + imgName;
  43. OutputStream out = new FileOutputStream(imgFilePath);
  44. out.write(b);
  45. out.flush();
  46. out.close();
  47. return true;
  48. } catch (Exception e) {
  49. return false;
  50. }
  51. }

  但是问题来了,当我的表格数据多的时候,发现导出来的图片已损坏。原因是字符串过长提交失败,网上的说法也不一致,有的说post限制2m的提交,要更改服务器配置(本人用的tomcat);也有说post无限制,无需修改。修改配置的方法我试过,没效果,无需修改?明明不行啊……

  经过多次的尝试,我发现转成Blob图片后使用ajax传输到后台并不会出现上述问题。而且用原生的ajax并非jquery封装过的ajax,代码如下:

  1. var node = document.getElementById('table');
  2. var responseText;
  3. domtoimage.toBlob(node)
  4. .then(function (blob) {
  5. var xhr = new XMLHttpRequest();
  6. xhr.open('POST', '/test', true);
  7. xhr.onreadystatechange = function(){
  8. if(xhr.readyState == 4 && xhr.status == 200){
  9. responseText = xhr.responseText;
  10. if(responseText != ""){
                    //拼servlet地址放入img标签的src属性中
  11. var reportUrl = "/EditorChartServlet?filename=" + responseText;
  12. $("img").attr("src",reportUrl);
  13. }
  14. }
  15. };
  16. xhr.setRequestHeader("Content-Type", "image/png");
  17. xhr.send(blob);
  18. });

  所以后台无需进行解码,而是在ajax里的url所请求的servlet中把Blob图片转存到指定文件夹中即可,servlet的代码如下:

  1. package ctx.ajax;
  2.  
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.OutputStream;
  7.  
  8. import javax.servlet.ServletException;
  9. import javax.servlet.annotation.WebServlet;
  10. import javax.servlet.http.HttpServlet;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletResponse;
  13.  
  14. @WebServlet(name = "TestUpload", urlPatterns = "/test")
  15. public class TestUpload extends HttpServlet {
  16. private static final long serialVersionUID = 1L;
  17.  
  18. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  19. String imgName = "table-test.png";
  20. String imgPath = MediaUtil.getImgPath();
  21. String imgFilePath = imgPath + "/" + imgName;
  22.  
  23. byte[] buffer = new byte[1024 * 1024];
  24.  
  25. InputStream input = request.getInputStream();
  26. OutputStream output = new FileOutputStream(imgFilePath);
  27. int bytesRead;
  28. while ((bytesRead = input.read(buffer)) != -1){
  29. // System.out.println(bytesRead);
  30. output.write(buffer, 0, bytesRead);
  31. }
  32. output.close();
  33. input.close();
  34.  
  35. response.getOutputStream().print(imgName);
  36. }
  37.  
  38. }

  

  三、浏览器根据文件名从服务器端获取图片

  Servlet的代码如下:

  1. package ctx.servlet;
  2.  
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;public class EditorChartServlet extends HttpServlet{
  9.  
  10. @Override
  11. protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  12. String filename = request.getParameter("filename");if (filename == null) {
  13. throw new ServletException("Parameter 'filename' must be supplied");
  14. }
  15.  
  16. filename = ServletUtilities.searchReplace(filename, "..", "");
  17.  
  18. String imgPath = MediaUtil.getImgPath();
  19. File file = new File(imgPath, filename);
  20. if (!(file.exists())) {
  21. throw new ServletException("File '" + file.getAbsolutePath() + "' does not exist");
  22. }
  23.  
  24. ServletUtilities.sendTempFile(file, response);
  25. }
  26. }

  所用到的ServletUtil方法代码如下:

  1. public static String searchReplace(String inputString, String searchString, String replaceString) {
  2. int i = inputString.indexOf(searchString);
  3. if (i == -1) {
  4. return inputString;
  5. }
  6.  
  7. String r = "";
  8. r = r + inputString.substring(0, i) + replaceString;
  9. if (i + searchString.length() < inputString.length()) {
  10. r = r + searchReplace(inputString.substring(i + searchString.length()), searchString, replaceString);
  11. }
  12.  
  13. return r;
  14. }
  15.  
  16. public static void sendTempFile(File file, HttpServletResponse response) throws IOException {
  17. String mimeType = null;
  18. String filename = file.getName();
  19. if (filename.length() > 5) {
  20. if (filename.substring(filename.length() - 5, filename.length()).equals(".jpeg")) {
  21. mimeType = "image/jpeg";
  22. } else if (filename.substring(filename.length() - 4, filename.length()).equals(".png")) {
  23. mimeType = "image/png";
  24. }
  25. }
  26. sendTempFile(file, response, mimeType);
  27. }
  28.  
  29. public static void sendTempFile(File file, HttpServletResponse response, String mimeType) throws IOException {
  30. if (file.exists()) {
  31. BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
  32.  
  33. if (mimeType != null) {
  34. response.setHeader("Content-Type", mimeType);
  35. }
  36. response.setHeader("Content-Length", String.valueOf(file.length()));
  37. SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
  38.  
  39. sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
  40. response.setHeader("Last-Modified", sdf.format(new Date(file.lastModified())));
  41.  
  42. BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
  43.  
  44. byte[] input = new byte[1024];
  45. boolean eof = false;
  46. while (!(eof)) {
  47. int length = bis.read(input);
  48. if (length == -1) {
  49. eof = true;
  50. } else {
  51. bos.write(input, 0, length);
  52. }
  53. }
  54. bos.flush();
  55. bis.close();
  56. bos.close();
  57. } else {
  58. throw new FileNotFoundException(file.getAbsolutePath());
  59. }
  60. }

  

  希望对大家有帮助,多多交流。

前端借助dom-to-image把HTML转成图片并通过ajax上传到服务器的更多相关文章

  1. 【Web】前端裁剪图片,并上传到服务器(Jcrop+canvas)

    web网站中常常有的功能:上传头像.上传封面等:一般图片都有一定的比例限制,所以需要前端在上传图片时,进行裁剪,并把裁剪后的图片进行上传. 本例采用Jcrop插件实现裁剪效果,canvas裁剪图片,并 ...

  2. 前端通信:ajax设计方案(三)--- 集成ajax上传技术

    在此之前让我感慨一下现在的前端开发的氛围.我遇到好多人,给我的观念都是,这个东西这个框架有了,那个东西那个框架做了,前端嘛,学几个框架,这个拼凑一下那个拼凑一下就好了.其实我想问,东西都框架做了,那你 ...

  3. 框架基础:ajax设计方案(三)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  4. 移动前端—H5实现图片先压缩再上传

    在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上 ...

  5. 前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组

    马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...

  6. 前端笔记之微信小程序(三)GET请求案例&文件上传和相册API&配置https

    一.信息流小程序-GET请求案例 1.1服务端接口开发 一定要养成接口的意识,前端单打独斗出不来任何效果,必须有接口配合,写一个带有分页.关键词查询的接口: 分页接口:http://127.0.0.1 ...

  7. python - django 使用ajax将图片上传到服务器并渲染到前端

    一.前端代码 <!doctype html> <html lang="en"> <head> <meta charset="UT ...

  8. HTML5 中已经可以用 Ajax 上传文件了,而且代码非常简单,借助 FormData 类即可发送文件数据。

    <?phpif (isset($_POST['upload'])) { var_dump($_FILES); move_uploaded_file($_FILES['upfile']['tmp_ ...

  9. 前端图片预览,上传前预览,兼容IE7、8、9、10、11,Firefox,Chrome(学习到的知识)

    文章地址:http://www.cnblogs.com/rubylouvre/p/4597344.html 一.window.URL 在Chrome中,window.URL和window.webkit ...

随机推荐

  1. Alamofire源码解读系列(三)之通知处理(Notification)

    本篇讲解swift中通知的用法 前言 通知作为传递事件和数据的载体,在使用中是不受限制的.由于忘记移除某个通知的监听,会造成很多潜在的问题,这些问题在测试中是很难被发现的.但这不是我们这篇文章探讨的主 ...

  2. (@WhiteTaken)UGUI中遇到的一些细碎的知识点

    最近接触Unity中UGUI的知识比较多,遇到的东西,就慢慢积累下来吧.用到就不用去网上找了. 1.Unity加载Sprite图片资源.在Unity中,我们可能会遇到,一张图片中,有多个UI,这时候导 ...

  3. Jquery 客户端生成验证码

    验证码的作用: 1.有效防止这种问题对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上是用验证码是现在很多网站通行的方式(比如招商银行的网上个人银行,腾讯的QQ社区),我们利用比较简 ...

  4. CoreAnimation 核心动画

    - (void)createBaseAnimation{ //基础动画 CABasicAnimation *animation = [CABasicAnimation animation]; anim ...

  5. 新学期的第一节Android课

    老师问,你们认为师生关系是什么样子的? 机智的我很快想到啦:或许是猫和老鼠的关系吧,嘿嘿O(∩_∩)O

  6. Oracle数据库笔记

    SQL分为四大类别 1.DDL:Date Definition Language 数据定义语言  用于建立.修改.删除数据库对象(create创建表和其它对象结构:alter修改表或其它结构:drop ...

  7. WPF 动态生成DataGrid及动态绑定解决方案

    一.场景 有过WPF项目经验的朋友可能都知道,如果一个DataGrid要绑定静态的数据是非常的简单的(所谓静态是指绑定的数据源的类型是静态的),如下图所示,想要显示产品数据,只需绑定到一个产品列表即可 ...

  8. pycharm社区版无database 解决方法

    第一步,点击file/setting/plugins 如下图所示 第二步,搜索database 安装database Nivagator 并Apply 第三步,新建数据库连接,open sql con ...

  9. Web Storage

    前面的话 Web存储最初作为HTML5的一部分被定义成API形式,但是后来被剥离出来作为独立的一份标准了.该标准目前还在草案阶段,但其中一部分内容已经被包括IE8在内的所有主流浏览器(可交互地)实现了 ...

  10. windows phone 8.1开发:锁屏提醒

    原文出自:http://www.bcmeng.com/lockscreen/ 之前小梦和大家分享了toast通知,磁铁更新,今天小梦和大家分享windows phone 8.1开发中的锁屏提醒.相比t ...