以前接到了一個任務,把jsp中的table轉成一個圖片,保存在指定文件夾並顯示在前端。html
個人思路是:1、引用第三方js在前端把table轉成圖片前端
2、經過ajax把圖片上傳到服務器,保存在指定文件夾java
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()); } }
但願對你們有幫助,多多交流。