前端借助dom-to-image把HTML转成图片并经过ajax上传到服务器

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

  个人思路是:1、引用第三方js在前端把table转成图片前端

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

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

  

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

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

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

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

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

  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); });

  不管个人表格有多大,它都能所有获取到,图片稍微失真。

 

  2、经过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);
    }

}

 

 

   

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

  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()); } }

 

  

  但愿对你们有帮助,多多交流。

相关文章
相关标签/搜索