[轉載自 https://www.cnblogs.com/CTXXCH/p/6721893.html]
前端借助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());
}
}

