Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲国产资源,中文字幕不卡在线观看,亚洲国产精品久久人人爱

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          SpringBoot集成echarts實現k線圖功能

          SpringBoot集成echarts實現k線圖功能

          .什么是echats?

          ECharts是一款基于JavaScript的數據可視化圖表庫,提供直觀,生動,可交互,可個性化定制的數據可視化圖表。 ECharts最初由百度團隊開源,并于2018年初捐贈給Apache基金會,成為ASF孵化級項目。 2021年1月26日晚,Apache基金會官方宣布ECharts項目正式畢業。

          2.代碼工程

          實驗目的

          實現股票日K線圖

          pom.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <parent>
                  <artifactId>springboot-demo</artifactId>
                  <groupId>com.et</groupId>
                  <version>1.0-SNAPSHOT</version>
              </parent>
              <modelVersion>4.0.0</modelVersion>
          
              <artifactId>echarts</artifactId>
          
              <properties>
                  <maven.compiler.source>8</maven.compiler.source>
                  <maven.compiler.target>8</maven.compiler.target>
              </properties>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
          
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-autoconfigure</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                      <scope>test</scope>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-thymeleaf</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                  </dependency>
              </dependencies>
          </project>

          controller

          訪問根目錄,跳轉到首頁

          package com.et.echats.controller;
          
          import org.springframework.stereotype.Controller;
          import org.springframework.web.bind.annotation.RequestMapping;
          
          
          @Controller
          public class HelloWorldController {
              @RequestMapping("/")
              public String index()
              {
                  return"index";
              }
          
          }

          index.html

          常量data0 可以通過接口從后端獲取,這里直接用靜態數據

          <!DOCTYPE html>
          <html lang="en">
             <head>
                <meta charset="UTF-8">
                <title>flask+echarts項目</title>
                <!-- 導入下載的 echarts.min.js -->
                <script src="../js/echarts.min.js"></script>
             </head>
             <body>
                <div id="main" style="width:100%;height:500px;"></div>
          
                <script type="text/javascript">
                   // 基于準備好的dom,初始化echarts實例
                   var myChart=echarts.init(document.getElementById('main'));
          
                   // 自定義圖表的寬高
                   // var myChart=echarts.init(document.getElementById('main'),null,{width:500,height:400});
          
                   // 跟隨瀏覽器的寬度自適應圖表大小
                   // var myChart=echarts.init(document.getElementById('main'));
                   // window.addEventListener('resize',function(){myChart.resize();});
          
                   const upColor="#ec0000";
                   const upBorderColor="#8A0000";
                   const downColor="#00da3c";
                   const downBorderColor="#008F28";
          
                   // 拆分數據
                   function splitData(rawData) {
                      // 類型年份數據
                      const categoryData=[];
                      // 開盤價,收盤價,最低價,最高價
                      const values=[];
                      for (var i=0; i < rawData.length; i++) {
                         categoryData.push(rawData[i].splice(0, 1)[0]);
                         values.push(rawData[i]);
                      }
                      return {
                         categoryData: categoryData,
                         values: values
                      }
                   };
          
                   // 數據解釋:時間,開盤價,收盤價,最低價,最高價
                   const data0=splitData([
                      ['2023/1/24', 2320.26, 2320.26, 2287.3, 2362.94],
                      ['2023/1/25', 2300, 2291.3, 2288.26, 2308.38],
                      ['2023/1/28', 2295.35, 2346.5, 2295.35, 2346.92],
                      ['2023/1/29', 2347.22, 2358.98, 2337.35, 2363.8],
                      ['2023/1/30', 2360.75, 2382.48, 2347.89, 2383.76],
                      ['2023/1/31', 2383.43, 2385.42, 2371.23, 2391.82],
                      ['2023/2/1', 2377.41, 2419.02, 2369.57, 2421.15],
                      ['2023/2/4', 2425.92, 2428.15, 2417.58, 2440.38],
                      ['2023/2/5', 2411, 2433.13, 2403.3, 2437.42],
                      ['2023/2/6', 2432.68, 2434.48, 2427.7, 2441.73],
                      ['2023/2/7', 2430.69, 2418.53, 2394.22, 2433.89],
                      ['2023/2/8', 2416.62, 2432.4, 2414.4, 2443.03],
                      ['2023/2/18', 2441.91, 2421.56, 2415.43, 2444.8],
                      ['2023/2/19', 2420.26, 2382.91, 2373.53, 2427.07],
                      ['2023/2/20', 2383.49, 2397.18, 2370.61, 2397.94],
                      ['2023/2/21', 2378.82, 2325.95, 2309.17, 2378.82],
                      ['2023/2/22', 2322.94, 2314.16, 2308.76, 2330.88],
                      ['2023/2/25', 2320.62, 2325.82, 2315.01, 2338.78],
                      ['2023/2/26', 2313.74, 2293.34, 2289.89, 2340.71],
                      ['2023/2/27', 2297.77, 2313.22, 2292.03, 2324.63],
                      ['2023/2/28', 2322.32, 2365.59, 2308.92, 2366.16],
                      ['2023/3/1', 2364.54, 2359.51, 2330.86, 2369.65],
                      ['2023/3/4', 2332.08, 2273.4, 2259.25, 2333.54],
                      ['2023/3/5', 2274.81, 2326.31, 2270.1, 2328.14],
                      ['2023/3/6', 2333.61, 2347.18, 2321.6, 2351.44],
                      ['2023/3/7', 2340.44, 2324.29, 2304.27, 2352.02],
                      ['2023/3/8', 2326.42, 2318.61, 2314.59, 2333.67],
                      ['2023/3/11', 2314.68, 2310.59, 2296.58, 2320.96],
                      ['2023/3/12', 2309.16, 2286.6, 2264.83, 2333.29],
                      ['2023/3/13', 2282.17, 2263.97, 2253.25, 2286.33],
                      ['2023/3/14', 2255.77, 2270.28, 2253.31, 2276.22],
                      ['2023/3/15', 2269.31, 2278.4, 2250, 2312.08],
                      ['2023/3/18', 2267.29, 2240.02, 2239.21, 2276.05],
                      ['2023/3/19', 2244.26, 2257.43, 2232.02, 2261.31],
                      ['2023/3/20', 2257.74, 2317.37, 2257.42, 2317.86],
                      ['2023/3/21', 2318.21, 2324.24, 2311.6, 2330.81],
                      ['2023/3/22', 2321.4, 2328.28, 2314.97, 2332],
                      ['2023/3/25', 2334.74, 2326.72, 2319.91, 2344.89],
                      ['2023/3/26', 2318.58, 2297.67, 2281.12, 2319.99],
                      ['2023/3/27', 2299.38, 2301.26, 2289, 2323.48],
                      ['2023/3/28', 2273.55, 2236.3, 2232.91, 2273.55],
                      ['2023/3/29', 2238.49, 2236.62, 2228.81, 2246.87],
                      ['2023/4/1', 2229.46, 2234.4, 2227.31, 2243.95],
                      ['2023/4/2', 2234.9, 2227.74, 2220.44, 2253.42],
                      ['2023/4/3', 2232.69, 2225.29, 2217.25, 2241.34],
                      ['2023/4/8', 2196.24, 2211.59, 2180.67, 2212.59],
                      ['2023/4/9', 2215.47, 2225.77, 2215.47, 2234.73],
                      ['2023/4/10', 2224.93, 2226.13, 2212.56, 2233.04],
                      ['2023/4/11', 2236.98, 2219.55, 2217.26, 2242.48],
                      ['2023/4/12', 2218.09, 2206.78, 2204.44, 2226.26],
                      ['2023/4/15', 2199.91, 2181.94, 2177.39, 2204.99],
                      ['2023/4/16', 2169.63, 2194.85, 2165.78, 2196.43],
                      ['2023/4/17', 2195.03, 2193.8, 2178.47, 2197.51],
                      ['2023/4/18', 2181.82, 2197.6, 2175.44, 2206.03],
                      ['2023/4/19', 2201.12, 2244.64, 2200.58, 2250.11],
                      ['2023/4/22', 2236.4, 2242.17, 2232.26, 2245.12],
                      ['2023/4/23', 2242.62, 2184.54, 2182.81, 2242.62],
                      ['2023/4/24', 2187.35, 2218.32, 2184.11, 2226.12],
                      ['2023/4/25', 2213.19, 2199.31, 2191.85, 2224.63],
                      ['2023/4/26', 2203.89, 2177.91, 2173.86, 2210.58],
                      ['2023/5/2', 2170.78, 2174.12, 2161.14, 2179.65],
                      ['2023/5/3', 2179.05, 2205.5, 2179.05, 2222.81],
                      ['2023/5/6', 2212.5, 2231.17, 2212.5, 2236.07],
                      ['2023/5/7', 2227.86, 2235.57, 2219.44, 2240.26],
                      ['2023/5/8', 2242.39, 2246.3, 2235.42, 2255.21],
                      ['2023/5/9', 2246.96, 2232.97, 2221.38, 2247.86],
                      ['2023/5/10', 2228.82, 2246.83, 2225.81, 2247.67],
                      ['2023/5/13', 2247.68, 2241.92, 2231.36, 2250.85],
                      ['2023/5/14', 2238.9, 2217.01, 2205.87, 2239.93],
                      ['2023/5/15', 2217.09, 2224.8, 2213.58, 2225.19],
                      ['2023/5/16', 2221.34, 2251.81, 2210.77, 2252.87],
                      ['2023/5/17', 2249.81, 2282.87, 2248.41, 2288.09],
                      ['2023/5/20', 2286.33, 2299.99, 2281.9, 2309.39],
                      ['2023/5/21', 2297.11, 2305.11, 2290.12, 2305.3],
                      ['2023/5/22', 2303.75, 2302.4, 2292.43, 2314.18],
                      ['2023/5/23', 2293.81, 2275.67, 2274.1, 2304.95],
                      ['2023/5/24', 2281.45, 2288.53, 2270.25, 2292.59],
                      ['2023/5/27', 2286.66, 2293.08, 2283.94, 2301.7],
                      ['2023/5/28', 2293.4, 2321.32, 2281.47, 2322.1],
                      ['2023/5/29', 2323.54, 2324.02, 2321.17, 2334.33],
                      ['2023/5/30', 2316.25, 2317.75, 2310.49, 2325.72],
                      ['2023/5/31', 2320.74, 2300.59, 2299.37, 2325.53],
                      ['2023/6/3', 2300.21, 2299.25, 2294.11, 2313.43],
                      ['2023/6/4', 2297.1, 2272.42, 2264.76, 2297.1],
                      ['2023/6/5', 2270.71, 2270.93, 2260.87, 2276.86],
                      ['2023/6/6', 2264.43, 2242.11, 2240.07, 2266.69],
                      ['2023/6/7', 2242.26, 2210.9, 2205.07, 2250.63],
                      ['2023/6/13', 2190.1, 2148.35, 2126.22, 2190.1]
                   ]);
          
                   // 計算MA(移動平均值)
                   function calaculateMA(dayCount) {
                      var result=[];
                      for (var i=0, len=data0.values.length; i < len; i++) {
                         if (i < dayCount) {
                            result.push("-");
                            continue;
                         }
                         var sum=0;
                         for (var j=0; j < dayCount; j++) {
                            sum +=data0.values[i - j][1];
                         }
                         result.push(Math.round(sum/dayCount));
                      }
                      return result
                   };
          
                   // 指定圖表的配置項和數據
                   var option={
                      // 圖表標題配置
                      title: {
                         text: 'K line graph'
                      },
                      // 提示框配置
                      tooltip: {
                         // 是否顯示提示框
                         show: true,
                         // 觸發類型,axis 移動到坐標軸就觸發
                         trigger: "axis",
                         // 坐標軸上提示點設置
                         axisPointer: {
                            type: "cross"
                         }
                      },
                      // 圖例配置
                      legend: {
                         data: ['day K', 'MA5', 'MA10', 'MA20', 'MA30']
                      },
                      // X 軸配置項
                      xAxis: {
                         type: "category",
                         data: data0.categoryData,
                         boundaryGap: false,
                         axisLine: {
                            onZero: false
                         },
                         splitLine: {
                            show: true
                         },
                         min: "dataMin",
                         max: "dataMax"
                      },
                      // y 軸配置項
                      yAxis: {
                         scale: true,
                         splitArea: {
                            show: true
                         }
                      },
                      dataZoom: [{
                            type: "inside",
                            start: 50,
                            end: 100
                         },
                         {
                            show: true,
                            type: "slider",
                            top: "90%",
                            start: 50,
                            end: 100
                         }
                      ],
                      // 系列配置,根據不同圖表有不同的配置
                      series: [
                         {
                         name: "day K",
                         // 圖表類型
                         type: 'candlestick',
                         // 數據內容
                         data: data0.values,
                         // K 線圖的圖形樣式
                         itemStyle: {
                            // 陽線 圖形的顏色
                            color: upColor,
                            // 陰線 圖形的顏色。
                            color0: downColor,
                            // 陽線 圖形的描邊顏色
                            borderColor: upBorderColor,
                            // 陰線 圖形的描邊顏色
                            borderVolor0: downBorderColor
                         },
                         // 標記點配置
                         markPoint: {
                            // 標注的文本
                            label: {
                               // 標簽內容格式器,支持字符串模板和回調函數兩種形式,字符串模板與回調函數返回的字符串均支持用 \n 換行。
                               // 參數 params 是 formatter 需要的單個數據集, value是傳入的數據值。
                               formatter: function(param) {
                                  return param !=null ? Math.round(param.value) + "" : "";
                               }
                            },
                            // 標注的數據數組。每個數組項是一個對象
                            data: [
                               // 用 coord 屬性指定數據在相應坐標系上的坐標位置
                               {
                                  name: "Mark",
                                  // 標注的坐標。坐標格式視系列的坐標系而定
                                  coord: ['2023/5/31', 2300],
                                  value: 2300,
                                  itemStyle: {
                                     color: 'rgb(41,60,85)'
                                  }
                               },
                               // 特殊的標注類型,用于標注最大值最小值等
                               {
                                  name: "highest value",
                                  type: "max",
                                  // 在使用 type 時有效,用于指定在哪個維度上指定最大值最小值。
                                  valueDim: "highest",
                               },
                               {
                                  name: 'lowest value',
                                  type: 'min',
                                  valueDim: 'lowest'
                               },
                               {
                                  name: 'average value on close',
                                  type: 'average',
                                  valueDim: 'close'
                               }
                            ],
                            // 提示框浮層內容格式器,支持字符串模板和回調函數兩種形式。
                            tooltip:{
                               // params 是 formatter 需要的數據集
                               formatter:function(param){
                                  // name數據名,類目名,data傳入的原始數據項
                                  return param.name+"<br>"+(param.data.coord || "")
                               }
                            }
                         },
                         markLine:{
                            // 標線兩端的標記類型,可以是一個數組分別指定兩端,也可以是單個統一指定
                            symbol:['none','none'],
                            // 標線的數據數組。每個數組項可以是一個兩個值的數組,分別表示線的起點和終點,每一項是一個對象
                            data:[
                               [
                                  // 定義從最小值到最大值的線
                                  {
                                     name:"from lowest to highest",
                                     type:"min",
                                     valueDim:"lowest",
                                     symbol:"circle",
                                     symbolSize: 10,
                                     label:{show:false},
                                     emphasis:{label:{show:false}}
                                  },
                                  {
                                     type:"max",
                                     valueDim:"highest",
                                     symbol:"circle",
                                     symbolSize:10,
                                     label:{show:false},
                                     emphasis:{label:{show:false}}
                                  },
                               ],
                               // 最小值水平線
                               {
                                  name:"min line on close",
                                  type:"min",
                                  valueDim:"close"
                               },
                               // 最大值水平線
                               {
                                  name:"max line on close",
                                  type:"max",
                                  valueDim:"close"
                               },
                            ],
                         }
                      },
                      {
                         name:"MA5",
                         type:"line",
                         data:calaculateMA(5),
                         smooth:true,
                         lineStyle:{opacity:0.5}
                      },
                      {
                         name:"MA10",
                         type:"line",
                         data:calaculateMA(10),
                         smooth:true,
                         lineStyle:{opacity:0.5}
                      },
                      {
                         name:"MA20",
                         type:"line",
                         data:calaculateMA(20),
                         smooth:true,
                         lineStyle:{opacity:0.5}
                      },
                      {
                         name:"MA30",
                         type:"line",
                         data:calaculateMA(30),
                         smooth:true,
                         lineStyle:{opacity:0.5}
                      },
                      ]
                   };
                   //使用剛指定的配置項和數據顯示圖像
                   myChart.setOption(option);
                </script>
             </body>
          </html>

          application.yaml

          spring.thymeleaf.cache=false
          spring.thymeleaf.suffix=.html

          以上只是一些關鍵代碼,所有代碼請參見下面代碼倉庫

          代碼倉庫

          • https://github.com/Harries/springboot-demo

          3.測試

          • 啟動Spring Boot應用
          • 訪問http://127.0.0.1:8080/

          4.引用

          • https://echarts.apache.org/handbook/en/basics/release-note/v5-feature/

          .什么是tika?

          Tika是一個內容分析工具,自帶全面的parser工具類,能解析基本所有常見格式的文件,得到文件的metadata,content等內容,返回格式化信息。總的來說可以作為一個通用的解析工具。特別對于搜索引擎的數據抓去和處理步驟有重要意義。Tika是Apache的Lucene項目下面的子項目,在lucene的應用中可以使用tika獲取大批量文檔中的內容來建立索引,非常方便,也很容易使用。Apache Tika toolkit可以自動檢測各種文檔(如word,ppt,xml,csv,ppt等)的類型并抽取文檔的元數據和文本內容。Tika集成了現有的文檔解析庫,并提供統一的接口,使針對不同類型的文檔進行解析變得更簡單。Tika針對搜索引擎索引、內容分析、轉化等非常有用。

          Tika架構

          應用程序員可以很容易地在他們的應用程序集成Tika。Tika提供了一個命令行界面和圖形用戶界面,使它比較人性化。在本章中,我們將討論構成Tika架構的四個重要模塊。下圖顯示了Tika的四個模塊的體系結構:

          • 語言檢測機制。
          • MIME檢測機制。
          • Parser接口。
          • Tika Facade 類.

          語言檢測機制

          每當一個文本文件被傳遞到Tika,它將檢測在其中的語言。它接受沒有語言的注釋文件和通過檢測該語言添加在該文件的元數據信息。支持語言識別,Tika 有一類叫做語言標識符在包org.apache.tika.language及語言識別資料庫里面包含了語言檢測從給定文本的算法。Tika 內部使用N-gram算法語言檢測。

          MIME檢測機制

          Tika可以根據MIME標準檢測文檔類型。Tika默認MIME類型檢測是使用org.apache.tika.mime.mimeTypes。它使用org.apache.tika.detect.Detector 接口大部分內容類型檢測。內部Tika使用多種技術,如文件匹配替換,內容類型提示,魔術字節,字符編碼,以及其他一些技術。

          解析器接口

          org.apache.tika.parser 解析器接口是Tika解析文檔的主要接口。該接口從提取文檔中的文本和元數據,并總結了其對外部用戶愿意寫解析器插件。采用不同的具體解析器類,具體為各個文檔類型,Tika 支持大量的文件格式。這些格式的具體類不同的文件格式提供支持,無論是通過直接實現邏輯分析器或使用外部解析器庫。

          Tika Facade 類

          使用的Tika facade類是從Java調用Tika的最簡單和直接的方式,而且也沿用了外觀的設計模式。可以在 Tika API的org.apache.tika包Tika 找到外觀facade類。通過實現基本用例,Tika作為facade的代理。它抽象了的Tika庫的底層復雜性,例如MIME檢測機制,解析器接口和語言檢測機制,并提供給用戶一個簡單的接口來使用。

          2.代碼工程

          實驗目標

          實現word文檔轉html

          pom.xml

          <?xml version="1.0" encoding="UTF-8"?>
          <project xmlns="http://maven.apache.org/POM/4.0.0"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
              <parent>
                  <artifactId>springboot-demo</artifactId>
                  <groupId>com.et</groupId>
                  <version>1.0-SNAPSHOT</version>
              </parent>
              <modelVersion>4.0.0</modelVersion>
          
              <artifactId>tika</artifactId>
          
              <properties>
                  <maven.compiler.source>8</maven.compiler.source>
                  <maven.compiler.target>8</maven.compiler.target>
              </properties>
              <dependencies>
          
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-web</artifactId>
                  </dependency>
          
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-autoconfigure</artifactId>
                  </dependency>
                  <dependency>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-starter-test</artifactId>
                      <scope>test</scope>
                  </dependency>
                  <dependency>
                      <groupId>org.apache.tika</groupId>
                      <artifactId>tika-parsers</artifactId>
                      <version>1.17</version>
                  </dependency>
                  <dependency>
                      <groupId>org.projectlombok</groupId>
                      <artifactId>lombok</artifactId>
                  </dependency>
          
              </dependencies>
          </project>

          controller

          package com.et.tika.controller;
          
          import com.et.tika.convertor.WordToHtmlConverter;
          import com.et.tika.dto.ConvertedDocumentDTO;
          import lombok.extern.slf4j.Slf4j;
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.web.bind.annotation.RequestMapping;
          import org.springframework.web.bind.annotation.RequestMethod;
          import org.springframework.web.bind.annotation.RequestParam;
          import org.springframework.web.bind.annotation.RestController;
          import org.springframework.web.multipart.MultipartFile;
          
          import java.util.HashMap;
          import java.util.Map;
          
          @RestController
          @Slf4j
          public class HelloWorldController {
              @RequestMapping("/hello")
              public Map<String, Object> showHelloWorld(){
                  Map<String, Object> map=new HashMap<>();
                  map.put("msg", "HelloWorld");
                  return map;
              }
              @Autowired
              WordToHtmlConverter converter;
          
          
          
              /**
               * Transforms the Word document into HTML document and returns the transformed document.
               *
               * @return  The content of the uploaded document as HTML.
               */
              @RequestMapping(value="/api/word-to-html", method=RequestMethod.POST)
              public ConvertedDocumentDTO convertWordDocumentIntoHtmlDocument(@RequestParam(value="file", required=true) MultipartFile wordDocument) {
                  log.info("Converting word document into HTML document");
          
                  ConvertedDocumentDTO htmlDocument=converter.convertWordDocumentIntoHtml(wordDocument);
          
                  log.info("Converted word document into HTML document.");
                  log.trace("The created HTML markup looks as follows: {}", htmlDocument);
          
                  return htmlDocument;
              }
          }

          WordToHtmlConverter

          package com.et.tika.convertor;
          
          
          import com.et.tika.dto.ConvertedDocumentDTO;
          import com.et.tika.exception.DocumentConversionException;
          import lombok.extern.slf4j.Slf4j;
          import org.apache.tika.exception.TikaException;
          import org.apache.tika.metadata.Metadata;
          import org.apache.tika.parser.ParseContext;
          import org.apache.tika.parser.Parser;
          import org.apache.tika.parser.microsoft.ooxml.OOXMLParser;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import org.springframework.stereotype.Component;
          import org.springframework.web.multipart.MultipartFile;
          import org.xml.sax.SAXException;
          
          import javax.xml.transform.OutputKeys;
          import javax.xml.transform.TransformerException;
          import javax.xml.transform.sax.SAXTransformerFactory;
          import javax.xml.transform.sax.TransformerHandler;
          import javax.xml.transform.stream.StreamResult;
          import java.io.IOException;
          import java.io.InputStream;
          import java.io.StringWriter;
          
          /**
           *
           */
          @Component
          @Slf4j
          public class WordToHtmlConverter {
          
          
              /**
               * Converts a .docx document into HTML markup. This code
               * is based on <a href="http://stackoverflow.com/a/9053258/313554">this StackOverflow</a> answer.
               *
               * @param wordDocument  The converted .docx document.
               * @return
               */
              public ConvertedDocumentDTO convertWordDocumentIntoHtml(MultipartFile wordDocument) {
                  log.info("Converting word document: {} into HTML", wordDocument.getOriginalFilename());
                  try {
                      InputStream input=wordDocument.getInputStream();
                      Parser parser=new OOXMLParser();
          
                      StringWriter sw=new StringWriter();
                      SAXTransformerFactory factory=(SAXTransformerFactory)
                              SAXTransformerFactory.newInstance();
                      TransformerHandler handler=factory.newTransformerHandler();
                      handler.getTransformer().setOutputProperty(OutputKeys.ENCODING, "utf-8");
                      handler.getTransformer().setOutputProperty(OutputKeys.METHOD, "html");
                      handler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
                      handler.setResult(new StreamResult(sw));
          
                      Metadata metadata=new Metadata();
                      metadata.add(Metadata.CONTENT_TYPE, "text/html;charset=utf-8");
                      parser.parse(input, handler, metadata, new ParseContext());
                      return new ConvertedDocumentDTO(wordDocument.getOriginalFilename(), sw.toString());
                  }
                  catch (IOException | SAXException | TransformerException | TikaException ex) {
                      log.error("Conversion failed because an exception was thrown", ex);
                      throw new DocumentConversionException(ex.getMessage(), ex);
                  }
              }
          }

          dto

          package com.et.tika.dto;
          
          import org.apache.commons.lang.builder.ToStringBuilder;
          
          /**
           *
           */
          public  class ConvertedDocumentDTO {
          
              private final String contentAsHtml;
              private final String filename;
          
              public ConvertedDocumentDTO(String filename, String contentAsHtml) {
                  this.contentAsHtml=contentAsHtml;
                  this.filename=filename;
              }
          
              public String getContentAsHtml() {
                  return contentAsHtml;
              }
          
              public String getFilename() {
                  return filename;
              }
          
              @Override
              public String toString() {
                  return new ToStringBuilder(this)
                          .append("filename", this.filename)
                          .append("contentAsHtml", this.contentAsHtml)
                          .toString();
              }
          }

          自定義異常

          package com.et.tika.exception;
          
          /**
           *
           */
          public final class DocumentConversionException extends RuntimeException {
          
              public DocumentConversionException(String message, Exception ex) {
                  super(message, ex);
              }
          }

          以上只是一些關鍵代碼,所有代碼請參見下面代碼倉庫

          代碼倉庫

          • https://github.com/Harries/springboot-demo

          3.測試

          啟動Spring Boot應用

          測試word轉html

          4.引用

          • https://tika.apache.org/
          • http://www.liuhaihua.cn/archives/710679.html

          前在SpringBoot項目中一直使用的是SpringFox提供的Swagger庫,上了下官網發現已經有接近兩年沒出新版本了!前幾天升級了SpringBoot 2.6.x 版本,發現這個庫的兼容性也越來越不好了,有的常用注解屬性被廢棄了居然都沒提供替代!無意中發現了另一款Swagger庫SpringDoc,試用了一下非常不錯,推薦給大家!

          SpringDoc簡介

          SpringDoc是一款可以結合SpringBoot使用的API文檔生成工具,基于OpenAPI 3,目前在Github上已有1.7K+Star,更新發版還是挺勤快的,是一款更好用的Swagger庫!值得一提的是SpringDoc不僅支持Spring WebMvc項目,還可以支持Spring WebFlux項目,甚至Spring Rest和Spring Native項目,總之非常強大,下面是一張SpringDoc的架構圖。

          使用

          接下來我們介紹下SpringDoc的使用,使用的是之前集成SpringFox的mall-tiny-swagger項目,我將把它改造成使用SpringDoc。

          集成

          首先我們得集成SpringDoc,在pom.xml中添加它的依賴即可,開箱即用,無需任何配置。

          <!--springdoc 官方Starter-->
          <dependency>
              <groupId>org.springdoc</groupId>
              <artifactId>springdoc-openapi-ui</artifactId>
              <version>1.6.6</version>
          </dependency>
          

          從SpringFox遷移

          • 我們先來看下經常使用的Swagger注解,看看SpringFox的和SpringDoc的有啥區別,畢竟對比已學過的技術能該快掌握新技術;

          • 接下來我們對之前Controller中使用的注解進行改造,對照上表即可,之前在@Api注解中被廢棄了好久又沒有替代的description屬性終于被支持了!
          /**
           * 品牌管理Controller
           * Created by macro on 2019/4/19.
           */
          @Tag(name="PmsBrandController", description="商品品牌管理")
          @Controller
          @RequestMapping("/brand")
          public class PmsBrandController {
              @Autowired
              private PmsBrandService brandService;
          
              private static final Logger LOGGER=LoggerFactory.getLogger(PmsBrandController.class);
          
              @Operation(summary="獲取所有品牌列表",description="需要登錄后訪問")
              @RequestMapping(value="listAll", method=RequestMethod.GET)
              @ResponseBody
              public CommonResult<List<PmsBrand>> getBrandList() {
                  return CommonResult.success(brandService.listAllBrand());
              }
          
              @Operation(summary="添加品牌")
              @RequestMapping(value="/create", method=RequestMethod.POST)
              @ResponseBody
              @PreAuthorize("hasRole('ADMIN')")
              public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
                  CommonResult commonResult;
                  int count=brandService.createBrand(pmsBrand);
                  if (count==1) {
                      commonResult=CommonResult.success(pmsBrand);
                      LOGGER.debug("createBrand success:{}", pmsBrand);
                  } else {
                      commonResult=CommonResult.failed("操作失敗");
                      LOGGER.debug("createBrand failed:{}", pmsBrand);
                  }
                  return commonResult;
              }
          
              @Operation(summary="更新指定id品牌信息")
              @RequestMapping(value="/update/{id}", method=RequestMethod.POST)
              @ResponseBody
              @PreAuthorize("hasRole('ADMIN')")
              public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
                  CommonResult commonResult;
                  int count=brandService.updateBrand(id, pmsBrandDto);
                  if (count==1) {
                      commonResult=CommonResult.success(pmsBrandDto);
                      LOGGER.debug("updateBrand success:{}", pmsBrandDto);
                  } else {
                      commonResult=CommonResult.failed("操作失敗");
                      LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
                  }
                  return commonResult;
              }
          
              @Operation(summary="刪除指定id的品牌")
              @RequestMapping(value="/delete/{id}", method=RequestMethod.GET)
              @ResponseBody
              @PreAuthorize("hasRole('ADMIN')")
              public CommonResult deleteBrand(@PathVariable("id") Long id) {
                  int count=brandService.deleteBrand(id);
                  if (count==1) {
                      LOGGER.debug("deleteBrand success :id={}", id);
                      return CommonResult.success(null);
                  } else {
                      LOGGER.debug("deleteBrand failed :id={}", id);
                      return CommonResult.failed("操作失敗");
                  }
              }
          
              @Operation(summary="分頁查詢品牌列表")
              @RequestMapping(value="/list", method=RequestMethod.GET)
              @ResponseBody
              @PreAuthorize("hasRole('ADMIN')")
              public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value="pageNum", defaultValue="1")
                                                                  @Parameter(description="頁碼") Integer pageNum,
                                                                  @RequestParam(value="pageSize", defaultValue="3")
                                                                  @Parameter(description="每頁數量") Integer pageSize) {
                  List<PmsBrand> brandList=brandService.listBrand(pageNum, pageSize);
                  return CommonResult.success(CommonPage.restPage(brandList));
              }
          
              @Operation(summary="獲取指定id的品牌詳情")
              @RequestMapping(value="/{id}", method=RequestMethod.GET)
              @ResponseBody
              @PreAuthorize("hasRole('ADMIN')")
              public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
                  return CommonResult.success(brandService.getBrand(id));
              }
          }
          
          • 接下來進行SpringDoc的配置,使用OpenAPI來配置基礎的文檔信息,通過GroupedOpenApi配置分組的API文檔,SpringDoc支持直接使用接口路徑進行配置。
          /**
           * SpringDoc API文檔相關配置
           * Created by macro on 2022/3/4.
           */
          @Configuration
          public class SpringDocConfig {
              @Bean
              public OpenAPI mallTinyOpenAPI() {
                  return new OpenAPI()
                          .info(new Info().title("Mall-Tiny API")
                                  .description("SpringDoc API 演示")
                                  .version("v1.0.0")
                                  .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
                          .externalDocs(new ExternalDocumentation()
                                  .description("SpringBoot實戰電商項目mall(50K+Star)全套文檔")
                                  .url("http://www.macrozheng.com"));
              }
          
              @Bean
              public GroupedOpenApi publicApi() {
                  return GroupedOpenApi.builder()
                          .group("brand")
                          .pathsToMatch("/brand/**")
                          .build();
              }
          
              @Bean
              public GroupedOpenApi adminApi() {
                  return GroupedOpenApi.builder()
                          .group("admin")
                          .pathsToMatch("/admin/**")
                          .build();
              }
          }
          

          結合SpringSecurity使用

          • 由于我們的項目集成了SpringSecurity,需要通過JWT認證頭進行訪問,我們還需配置好SpringDoc的白名單路徑,主要是Swagger的資源路徑;
          /**
           * SpringSecurity的配置
           * Created by macro on 2018/4/26.
           */
          @Configuration
          @EnableWebSecurity
          @EnableGlobalMethodSecurity(prePostEnabled=true)
          public class SecurityConfig extends WebSecurityConfigurerAdapter {                                              
          
              @Override
              protected void configure(HttpSecurity httpSecurity) throws Exception {
                  httpSecurity.csrf()// 由于使用的是JWT,我們這里不需要csrf
                          .disable()
                          .sessionManagement()// 基于token,所以不需要session
                          .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                          .and()
                          .authorizeRequests()
                          .antMatchers(HttpMethod.GET, // Swagger的資源路徑需要允許訪問
                                  "/",   
                                  "/swagger-ui.html",
                                  "/swagger-ui/",
                                  "/*.html",
                                  "/favicon.ico",
                                  "/**/*.html",
                                  "/**/*.css",
                                  "/**/*.js",
                                  "/swagger-resources/**",
                                  "/v3/api-docs/**"
                          )
                          .permitAll()
                          .antMatchers("/admin/login")// 對登錄注冊要允許匿名訪問
                          .permitAll()
                          .antMatchers(HttpMethod.OPTIONS)//跨域請求會先進行一次options請求
                          .permitAll()
                          .anyRequest()// 除上面外的所有請求全部需要鑒權認證
                          .authenticated();
                  
              }
          
          }
          
          • 然后在OpenAPI對象中通過addSecurityItem方法和SecurityScheme對象,啟用基于JWT的認證功能。
          /**
           * SpringDoc API文檔相關配置
           * Created by macro on 2022/3/4.
           */
          @Configuration
          public class SpringDocConfig {
              private static final String SECURITY_SCHEME_NAME="BearerAuth";
              @Bean
              public OpenAPI mallTinyOpenAPI() {
                  return new OpenAPI()
                          .info(new Info().title("Mall-Tiny API")
                                  .description("SpringDoc API 演示")
                                  .version("v1.0.0")
                                  .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
                          .externalDocs(new ExternalDocumentation()
                                  .description("SpringBoot實戰電商項目mall(50K+Star)全套文檔")
                                  .url("http://www.macrozheng.com"))
                          .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
                          .components(new Components()
                                          .addSecuritySchemes(SECURITY_SCHEME_NAME,
                                                  new SecurityScheme()
                                                          .name(SECURITY_SCHEME_NAME)
                                                          .type(SecurityScheme.Type.HTTP)
                                                          .scheme("bearer")
                                                          .bearerFormat("JWT")));
              }
          }
          

          測試

          • 接下來啟動項目就可以訪問Swagger界面了,訪問地址:http://localhost:8088/swagger-ui.html

          • 我們先通過登錄接口進行登錄,可以發現這個版本的Swagger返回結果是支持高亮顯示的,版本明顯比SpringFox來的新;

          • 然后通過認證按鈕輸入獲取到的認證頭信息,注意這里不用加bearer前綴;

          • 之后我們就可以愉快地訪問需要登錄認證的接口了;

          • 看一眼請求參數的文檔說明,還是熟悉的Swagger樣式!

          常用配置

          SpringDoc還有一些常用的配置可以了解下,更多配置可以參考官方文檔。

          springdoc:
            swagger-ui:
              # 修改Swagger UI路徑
              path: /swagger-ui.html
              # 開啟Swagger UI界面
              enabled: true
            api-docs:
              # 修改api-docs路徑
              path: /v3/api-docs
              # 開啟api-docs
              enabled: true
            # 配置需要生成接口文檔的掃描包
            packages-to-scan: com.macro.mall.tiny.controller
            # 配置需要生成接口文檔的接口路徑
            paths-to-match: /brand/**,/admin/**
          

          總結

          在SpringFox的Swagger庫好久不出新版的情況下,遷移到SpringDoc確實是一個更好的選擇。今天體驗了一把SpringDoc,確實很好用,和之前熟悉的用法差不多,學習成本極低。而且SpringDoc能支持WebFlux之類的項目,功能也更加強大,使用SpringFox有點卡手的朋友可以遷移到它試試!

          參考資料

          • 項目地址:https://github.com/springdoc/springdoc-openapi
          • 官方文檔:https://springdoc.org/

          項目源碼地址

          https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-springdoc

          來源:https://mp.weixin.qq.com/s/scityFhZp9BOJorZSdCG0w


          主站蜘蛛池模板: 欧美日韩综合一区二区三区| 久久精品道一区二区三区| 成人午夜视频精品一区| 春暖花开亚洲性无区一区二区| 人妻无码久久一区二区三区免费| 成人欧美一区二区三区在线视频 | 亚洲一区二区三区免费在线观看 | 天天看高清无码一区二区三区 | 国产成人一区二区三中文| 丰满爆乳一区二区三区| 国产伦一区二区三区免费| 中文字幕在线不卡一区二区| 亚欧免费视频一区二区三区| 午夜影视日本亚洲欧洲精品一区 | 熟妇人妻系列av无码一区二区| 日本人真淫视频一区二区三区| 免费一本色道久久一区| 好爽毛片一区二区三区四无码三飞| 精品人妻系列无码一区二区三区| 亚洲av日韩综合一区在线观看| 亚洲国产精品无码久久一区二区| 亚洲熟妇av一区二区三区| 久久精品一区二区东京热| 本免费AV无码专区一区| 亚洲日韩激情无码一区 | 国产在线精品观看一区| 麻豆国产在线不卡一区二区| 亚洲AV无码一区二区三区网址 | 中文日韩字幕一区在线观看| 中文字幕精品一区| 久久久国产精品一区二区18禁| 亚洲AV无码一区二区乱子伦| 一区二区三区在线免费看| 3D动漫精品一区二区三区| 国精产品一区一区三区有限在线| 精品国产天堂综合一区在线| 免费观看日本污污ww网站一区| 亚洲一区精品伊人久久伊人| 久久久精品一区二区三区| 激情亚洲一区国产精品| 国产精品久久久久一区二区三区 |