前端借助dom-to-image把HTML转成图片并通过ajax上传到服务器
之前接到了一个任务,把jsp中的table转成一个图片,保存在指定文件夹并显示在前端。
我的思路是:一、引用第三方js在前端把table转成图片
一、引用第三方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());
}
}
希望对大家有帮助,多多交流。
前端借助dom-to-image把HTML转成图片并通过ajax上传到服务器的更多相关文章
- 【Web】前端裁剪图片,并上传到服务器(Jcrop+canvas)
web网站中常常有的功能:上传头像.上传封面等:一般图片都有一定的比例限制,所以需要前端在上传图片时,进行裁剪,并把裁剪后的图片进行上传. 本例采用Jcrop插件实现裁剪效果,canvas裁剪图片,并 ...
- 前端通信:ajax设计方案(三)--- 集成ajax上传技术
在此之前让我感慨一下现在的前端开发的氛围.我遇到好多人,给我的观念都是,这个东西这个框架有了,那个东西那个框架做了,前端嘛,学几个框架,这个拼凑一下那个拼凑一下就好了.其实我想问,东西都框架做了,那你 ...
- 框架基础:ajax设计方案(三)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组
马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...
- 移动前端—H5实现图片先压缩再上传
在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上 ...
- 前端通信:ajax设计方案(四)--- 集成ajax上传技术 大文件/超大文件前端切割上传,后端进行重组
马上要过年了,哎,回家的心情也特别的激烈.有钱没钱,回家过年,家永远是舔舐伤口最好的地方.新的一年继续加油努力. 上次做了前端的ajax的上传文件技术,支持单文件,多文件上传,并对文件的格式和大小进行 ...
- 前端笔记之微信小程序(三)GET请求案例&文件上传和相册API&配置https
一.信息流小程序-GET请求案例 1.1服务端接口开发 一定要养成接口的意识,前端单打独斗出不来任何效果,必须有接口配合,写一个带有分页.关键词查询的接口: 分页接口:http://127.0.0.1 ...
- python - django 使用ajax将图片上传到服务器并渲染到前端
一.前端代码 <!doctype html> <html lang="en"> <head> <meta charset="UT ...
- HTML5 中已经可以用 Ajax 上传文件了,而且代码非常简单,借助 FormData 类即可发送文件数据。
<?phpif (isset($_POST['upload'])) { var_dump($_FILES); move_uploaded_file($_FILES['upfile']['tmp_ ...
- 前端图片预览,上传前预览,兼容IE7、8、9、10、11,Firefox,Chrome(学习到的知识)
文章地址:http://www.cnblogs.com/rubylouvre/p/4597344.html 一.window.URL 在Chrome中,window.URL和window.webkit ...
随机推荐
- C#基础 运算符
运算符分为5类-- 1.算数运算符[加加(++) 减减(--) 加(+) 减(-) 乘(*) 除(/) 取余(%)] (1)前++和后++的区别 using System; using ...
- VisualStudio2017下ASP.NET CORE的TagHelper智能提示解决办法
之前在VS2017RC中就发现该问题,安装了依赖,但是前段一直点不出来asp-for,后来查了发行说明, 才知道在VS2017rc中暂时无法解决,所以一直等到VS2017正式版的发布,急冲冲的装好, ...
- Pdf File Writer 中文应用(PDF文件编写器C#类库)
该文由小居工作室(QQ:2482052910) 翻译并提供解答支持,原文地址:Pdf File Writer 中文应用(PDF文件编写器C#类库):http://www.cnblogs.com/ ...
- Mac OSX Sierra WiFi connecting problem
吐槽一下,苹果的质量管控越来越差了. Mac OSX Sierra有时突然或升级后会遇到wifi不停重连连不上问题,现象为不停地连接wifi. 网上有人说删除 /Library/Preferences ...
- Spring总结_04_容器和bean
一.概念理解 1.容器 IoC容器负责容纳并管理bean,在Spring中,BeanFactory是IoC容器的核心接口. 它的职责包括:实例化.定位.配置应用程序中的对象及建立这些对象间的依赖. ...
- 读learning spark lighting chapter1~chapter2
chapter 1 introduction to the analysis with spark the conponents of Sparks spark core(contains the b ...
- ES6是什么
编程语言JavaScript是ECMAScript的实现和扩展,由ECMA(一个类似W3C的标准组织)参与进行标准化.编程语言JavaScript是ECMAScript的实现和扩展,由ECMA(一个类 ...
- Linux块设备IO子系统(二) _页高速缓存
磁盘驱动就是实现磁盘空间和内存空间数据上的交互,在上一篇中我们讨论了内存端的Page Segment Block Sector相关的概念,本文以3.14内核为例,讨论这部分内存是如何被组织管理的.我们 ...
- 学习CSS了解单位em和px的区别
这里引用的是Jorux的“95%的中国网站需要重写CSS”的文章,题目有点吓人,但是确实是现在国内网页制作方面的一些缺陷.我一直也搞不清楚px与em之间的关系和特点,看过以后确实收获很大.平时都是用p ...
- 读书笔记 effective c++ Item 38 通过组合(composition)为 “has-a”或者“is-implemented-in-terms-of”建模
1. 什么是组合(composition)? 组合(composition)是一种类型之间的关系,这种关系当一种类型的对象包含另外一种类型的对象时就会产生.举个例子: class Address { ...