java 導出word 包含echarts圖表

来源:https://www.cnblogs.com/junnnnnnnn/archive/2022/08/25/16624351.html
-Advertisement-
Play Games

你們知道,“當任務數超過線程池的核心線程數時,如何讓它不進入隊列,而是直接啟用最大線程數”嗎? 大家好,我是Mic,一個工作了14年的Java程式員。 剛剛這個問題是一個工作5年的粉絲最近去某互聯網公司面試遇到的。 關於這個問題,我把高手的回答整理到了一個15W字的面試文檔裡面。 大家可以私信我領取 ...


需求是導出word,裡面有數據統計圖表。

要從後端直接導出圖表的話,思路是這樣的:

  先通過echarts生成圖片,通過phantomjs 截圖,將圖片暫存在本地,再將圖片轉換成base64,然後放入word。

 

phantomjs 是一個基於js的webkit內核無頭瀏覽器 也就是沒有顯示界面的瀏覽器。

 

一、準備word模板,轉換成xml,需要填入數據的地方用${欄位},需要天出圖片的地方可以先隨便一張用圖片替代,方便之後找到圖片插入位置。這裡就不多說了

 

二、準備環境、依賴

 1、準備js,需要用到的,放在同一個文件夾下麵。自己更改echarts-convert.js的路徑

  

 

 

   echarts.min.js 和jquery.js 去官網下載

  echarts-convert.js的內容如下:

  

(function () {
    var system = require('system');
    var fs = require('fs');
    var config = {
        // define the location of js files
        //這樣寫要求這三個js在同一個文件夾下
        JQUERY: 'jquery-3.5.1.min.js',
        //ESL: 'esl.js',
        ECHARTS: 'echarts.min.js',
        // default container width and height
        DEFAULT_WIDTH: '400',
        DEFAULT_HEIGHT: '600'
    }, parseParams, render, pick, usage;

    usage = function () {
        console.log("\nUsage: phantomjs echarts-convert.js -options options -outfile filename -width width -height height"
            + "OR"
            + "Usage: phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height\n");
    };

    pick = function () {
        var args = arguments, i, arg, length = args.length;
        for (i = 0; i < length; i += 1) {
            arg = args[i];
            if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
                return arg;
            }
        }
    };

    parseParams = function () {
        var map = {}, i, key;
        if (system.args.length < 2) {
            usage();
            phantom.exit();
        }
        for (i = 0; i < system.args.length; i += 1) {
            if (system.args[i].charAt(0) === '-') {
                key = system.args[i].substr(1, i.length);
                if (key === 'infile') {
                    // get string from file
                    // force translate the key from infile to options.
                    key = 'options';
                    try {
                        map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
                    } catch (e) {
                        console.log('Error: cannot find file, ' + system.args[i + 1]);
                        phantom.exit();
                    }
                } else {
                    map[key] = system.args[i + 1].replace(/^\s+/, '');
                }
            }
        }
        return map;
    };

    render = function (params) {
        var page = require('webpage').create(), createChart;

        var bodyMale = config.SVG_MALE;
        page.onConsoleMessage = function (msg) {
            console.log(msg);
        };

        page.onAlert = function (msg) {
            console.log(msg);
        };

        createChart = function (inputOption, width, height,config) {
            var counter = 0;
            function decrementImgCounter() {
                counter -= 1;
                if (counter < 1) {
                    console.log(messages.imagesLoaded);
                }
            }

            function loadScript(varStr, codeStr) {
                var script = $('<script>').attr('type', 'text/javascript');
                script.html('var ' + varStr + ' = ' + codeStr);
                document.getElementsByTagName("head")[0].appendChild(script[0]);
                if (window[varStr] !== undefined) {
                    console.log('Echarts.' + varStr + ' has been parsed');
                }
            }

            function loadImages() {
                var images = $('image'), i, img;
                if (images.length > 0) {
                    counter = images.length;
                    for (i = 0; i < images.length; i += 1) {
                        img = new Image();
                        img.onload = img.onerror = decrementImgCounter;
                        img.src = images[i].getAttribute('href');
                    }
                } else {
                    console.log('The images have been loaded');
                }
            }
            // load opitons
            if (inputOption != 'undefined') {
                // parse the options
                loadScript('options', inputOption);
                // disable the animation
                options.animation = false;
            }

            // we render the image, so we need set background to white.
            $(document.body).css('backgroundColor', 'white');
            var container = $("<div>").appendTo(document.body);
            container.attr('id', 'container');
            container.css({
                width: width,
                height: height
            });
            // render the chart
            var myChart = echarts.init(container[0]);
            myChart.setOption(options);
            // load images
            loadImages();
            return myChart.getDataURL();
        };

        // parse the params
        page.open("about:blank", function (status) {
            // inject the dependency js
            page.injectJs(config.ESL);
            page.injectJs(config.JQUERY);
            page.injectJs(config.ECHARTS);


            var width = pick(params.width, config.DEFAULT_WIDTH);
            var height = pick(params.height, config.DEFAULT_HEIGHT);

            // create the chart
            var base64 = page.evaluate(createChart, params.options, width, height,config);
            fs.write("base64.txt",base64);
            // define the clip-rectangle
            page.clipRect = {
                top: 0,
                left: 0,
                width: width,

                height: height
            };
            // render the image
            page.render(params.outfile);
            console.log('render complete:' + params.outfile);
            // exit
            phantom.exit();
        });
    };
// get the args
    var params = parseParams();

// validate the params
    if (params.options === undefined || params.options.length === 0) {
        console.log("ERROR: No options or infile found.");
        usage();
        phantom.exit();
    }
// set the default out file
    if (params.outfile === undefined) {
        var tmpDir = fs.workingDirectory + '/tmp';
        // exists tmpDir and is it writable?
        if (!fs.exists(tmpDir)) {
            try {
                fs.makeDirectory(tmpDir);
            } catch (e) {
                console.log('ERROR: Cannot make tmp directory');
            }
        }
        params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
    }

// render the image
    render(params);
}());

 

  2、安裝phantomjs

    網站:https://phantomjs.org/download.html

    選擇合適的版本,我下載的是windows的,下載解壓

    

 

 

     配置環境變數:

 

 

 

 

     

 

 

   

    然後測試是否安裝成功,在命令行 輸入:phantomjs -- version

    

 

 

     出現版本就表示安裝成功了。

  

  3、構建echarts數據,就是option

    可以看原文檔:https://gitee.com/free/ECharts#https://gitee.com/link?target=http%3A%2F%2Fblog.csdn.net%2Fisea533%2Farticle%2Fdetails%2F43225717

    

<dependency>
     <groupId>com.github.abel533</groupId>
     <artifactId>ECharts</artifactId>
     <version>3.0.0.6</version>
</dependency>                

 

三、生成echarts圖表

  1、構建echarts 的數據option

   

    /**
     * 構建柱狀圖
     * @param cate x軸數據
     * @param enterpriseCnt 數據
     * @return
     */
    private static String getOption(List<String> cate, List<Integer> enterpriseCnt){
        GsonOption option = new GsonOption();
        option.title().setText("測試點位數圖片"); // 標題
        // 設置圖例
        option.legend().data("點位數").x("center");

        Bar bar = new Bar();
        // x軸
        CategoryAxis category = new CategoryAxis();
        category.data(cate.toArray());

        //y 軸
        ValueAxis valueAxis = new ValueAxis();

        bar.data().addAll(enterpriseCnt); // 數據

        option.xAxis(category);  //x軸
        option.yAxis(valueAxis);  //y軸

        option.series(bar);// 多條柱子放多個bar就行

        String optionStr = option.toString().replace(" ", "");
        log.info(optionStr);
        return optionStr;
    }

  2、生成echarts圖片

  

    public static String generateEChart(String option, String filepath, String fileName) {
        String dataPath = writeOptionToFile(option, filepath); // 將option 數據寫磁碟上用文件保存
        String path = filepath + fileName+".png"; //生成圖片路徑
        try {
            String JSpath = "D:\\data\\tmp\\echarts-convert.js";
            String cmd = "phantomjs " + JSpath + " -infile " + dataPath + " -outfile " + path;
            log.info(cmd);
            Process process = Runtime.getRuntime().exec(cmd);
            BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
            String line = "";
            while ((line = input.readLine()) != null) {
                log.info(line);
            }
            input.close();
            // 刪除json數據
            File jsonFile = new File(dataPath);
            jsonFile.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return path;
    }

  

    public static String writeOptionToFile(String options, String filepath) {
        String dataPath = filepath +"echart.json";
        try {
            File writename = new File(dataPath);
            if (!writename.getParentFile().exists()) {   //文件目錄不存在,則先創建,不然會報錯
                writename.getParentFile().mkdirs();
            }
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(writename), "UTF-8"));
            out.write(options);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return dataPath;
    }

 

  先測試一哈:

   

  這個時候在文件夾下可以看到圖片啦:

  

 

 

 

四、導出word

  準備好數據,圖片需要base64編碼的,將之前生成的echart圖片讀入轉base64

  

    public static String imageToBase64(String imgPath) {
        InputStream in = null;
        byte[] data = null;
        try {
            in = new FileInputStream(imgPath);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
        Base64Encoder encoder = new Base64Encoder();
        return encoder.encode(data);
    }

  

  然後就是正常word的導出了

  

 

   

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Java集合09 18.TreeSet 元素無序:插入順序和輸出順序不一致 可以按照一定的規則進行排序,具體排序方式取決於構造方法: TreeSet () :根據其元素的自然排序進行排序 TreeSet (Comparator comparator) :根據指定的比較器進行排序 沒有帶索引的方法,所 ...
  • 1、獲取bean實例的三種方式 1.1 id 屬性 1.1.1 jar <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.18.RELEASE</sp ...
  • 最近寫項目發現pythoncom 導入 win32api、win32con、win32com、win32gui 等win32相關的包都會出現或多或少問題,Google一大堆不靠譜的方法試了個遍。 特此記錄一下解決辦法 ...
  • 前言 1,在一些特定的場景我們往往需要看一下介面的入參,特別是跨系統的介面調用(下發,推送),這個時候的介面入參就很重要,我們保存入參入庫,如果出問題就可以馬上定位是上游還是下游的問題(方便扯皮) 2,還有一般需要在系統中看普通日誌,還有特殊的異常(報錯)日誌,一般我們可以通過伺服器去查看相應的位置 ...
  • 泛型—— 一種可以接收數據類型的數據類型,本文將通俗講解Java泛型的優點、方法及相關細節。 一、泛型的引入 我們都知道,繼承是面向對象的三大特性之一,比如在我們向集合中添加元素的過程中add()方法里填入的是Object類,而Object又是所有類的父類,這就產生了一個問題——添加的類型無法做到統 ...
  • 在練習格式化輸出時出現錯誤TypeError: can't multiply sequence by non-int of type 'float' 為什麼會出現TypeError:不能將序列乘以’float’類型的非整數? 實際情況是,這裡隱藏著一個優先順序的問題 我們發現一般情況下我們在格式化輸出 ...
  • docker-compose容器編排 為什麼使用docker-compose ​ 我們學會了使用 dockerfile 構建 docker 鏡像,看起來已經能夠滿足我們的日常需求了,無論需要什麼環境,在 dockerfile 里逐步構建,然後 build、run,就 ok 了,也滿足了我們docke ...
  • 《零起點Python機器學習快速入門》PDF高清版下載地址 內容簡介 · · · · · · 《零起點Python機器學習快速入門》採用獨創的黑箱模式,MBA案例教學機制,結合一線實戰案例,介紹 Sklearn人工智慧模塊庫和常用的機器學習演算法。《零起點Python機器學習快速入門》中配備大量圖表說 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...