文档章节

dom-to-image把HTML转成图片并通过ajax上传到服务器

o
 osc_fmg49rzg
发布于 2019/03/20 09:43
字数 1470
阅读 54
收藏 0

精选30+云产品,助力企业轻松上云!>>>

[转载自 https://www.cnblogs.com/CTXXCH/p/6721893.html]

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

 

  之前接到了一个任务,把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):

  

复制代码
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());
        }
    }
复制代码
o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
dom-to-image把HTML转成图片并通过ajax上传到服务器

[转载自 https://www.cnblogs.com/CTXXCH/p/6721893.html] 前端借助dom-to-image把HTML转成图片并通过ajax上传到服务器   之前接到了一个任务,把jsp中的table转成一个图片,保存在指定文件...

清风eq
2019/03/20
0
0
初探JavaScript的截屏实现

最近参与了网易炉石盒子的相关页面开发,在做卡组分享页(地址:炉石盒子卡组分享),有个需求:用户可以把这个卡组以图片的形式分享给好友。最初的的做法是使用服务器把该页面转换成图片,然...

xsster
2017/07/28
0
0
十个非常好用的文件上传工具(插件)

根据维基百科的定义,文件上传是将本地的数据传送到远程系统的过程,比如一个服务器。此外,远程系统还需将任何正在上传的文件复制一个备份。下面列举了十个非常好用的文件上传工具,它们有些...

未来更宽广
2014/09/15
2
0
Ajax 实现上传图片即时预览功能

原文出自:http://www.pm-road.com/index.php/2014/07/31/50/ 很多网站在上传头像或照片的时候,都会有一个预览功能,结合自身体验将该功能实现一下;要求:图片保存到数据库 之前,我在做项...

pm-road
2014/12/17
301
0
上传文件前端

XMLHttpRequest 感受一下原生JavaScript编写的ajax请求: base64字符串前缀类似:data:image/jpeg;base64, 上述代码dataURItoBlob方法通过base64编码转blob,如果想要知道如何从file/blob转b...

tyosc
2019/07/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

asp.net core之NLog

NuGet添加 NLog.Web.AspNetCore。 <PackageReference Include="Microsoft.AspNetCore.App" /> 添加配置文件 新建一个文件nlog.config(建议全部小写,linux系统中要注意), 并右键点击其属性......

一介草民Coder
30分钟前
23
0
.NET中的struct和class有什么区别? - What's the difference between struct and class in .NET?

问题: .NET中的struct和class有什么区别? 解决方案: 参考一: https://stackoom.com/question/3OT/NET中的struct和class有什么区别 参考二: https://oldbug.net/q/3OT/What-s-the-differ...

富含淀粉
今天
23
0
android:layout_weight是什么意思? - What does android:layout_weight mean?

问题: I don't understand how to use this attribute. 我不明白如何使用这个属性。 Can anyone tell me more about it? 谁能告诉我更多关于它的事情? 解决方案: 参考一: https://stacko...

javail
今天
17
0
CSS背景不透明度[重复] - CSS Background Opacity [duplicate]

问题: This question already has an answer here: 这个问题已经在这里有了答案: How do I give text or an image a transparent background using CSS? 如何使用CSS为文本或图像提供透明背...

fyin1314
今天
31
0
node http 获取gb2312网页如何转为utf8

最初,我想当然认为是下述做法,但被证明是错误的 const http = require('http'), iconv = require('iconv-lite');const url = 'http://xxx';http.get(url, function(res) { var bo......

高延
今天
24
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部