zhangding 2 жил өмнө
parent
commit
8c66aa7e4f

+ 86 - 32
master/pom.xml

@@ -35,7 +35,7 @@
 		<swagger.version>2.9.2</swagger.version>
 		<poi.version>3.17</poi.version>
 		<oshi.version>5.2.5</oshi.version>
-        <jna.version>5.5.0</jna.version>
+		<jna.version>5.5.0</jna.version>
 		<velocity.version>1.7</velocity.version>
 		<oracle.version>11.2.0.3</oracle.version>
 		<lombok.version>1.18.4</lombok.version>
@@ -121,10 +121,10 @@
 
 		<!--阿里数据库连接池 -->
 		<dependency>
-            <groupId>com.alibaba</groupId>
-            <artifactId>druid-spring-boot-starter</artifactId>
-            <version>${druid.version}</version>
-        </dependency>
+			<groupId>com.alibaba</groupId>
+			<artifactId>druid-spring-boot-starter</artifactId>
+			<version>${druid.version}</version>
+		</dependency>
 
 		<!--常用工具类 -->
 		<dependency>
@@ -166,7 +166,7 @@
 			<artifactId>spring-context-support</artifactId>
 		</dependency>
 
-        <!--Token生成与解析-->
+		<!--Token生成与解析-->
 		<dependency>
 			<groupId>io.jsonwebtoken</groupId>
 			<artifactId>jjwt</artifactId>
@@ -179,29 +179,29 @@
 			<artifactId>springfox-swagger2</artifactId>
 			<version>${swagger.version}</version>
 			<exclusions>
-			    <exclusion>
-			        <groupId>io.swagger</groupId>
-			        <artifactId>swagger-annotations</artifactId>
-			    </exclusion>
-			    <exclusion>
-			        <groupId>io.swagger</groupId>
-			        <artifactId>swagger-models</artifactId>
-			    </exclusion>
+				<exclusion>
+					<groupId>io.swagger</groupId>
+					<artifactId>swagger-annotations</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>io.swagger</groupId>
+					<artifactId>swagger-models</artifactId>
+				</exclusion>
 			</exclusions>
 		</dependency>
 
 		<!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
-        <dependency>
-            <groupId>io.swagger</groupId>
-            <artifactId>swagger-annotations</artifactId>
-            <version>1.5.21</version>
-        </dependency>
-
-        <dependency>
-            <groupId>io.swagger</groupId>
-            <artifactId>swagger-models</artifactId>
-            <version>1.5.21</version>
-        </dependency>
+		<dependency>
+			<groupId>io.swagger</groupId>
+			<artifactId>swagger-annotations</artifactId>
+			<version>1.5.21</version>
+		</dependency>
+
+		<dependency>
+			<groupId>io.swagger</groupId>
+			<artifactId>swagger-models</artifactId>
+			<version>1.5.21</version>
+		</dependency>
 
 		<!-- swagger2-UI-->
 		<dependency>
@@ -210,18 +210,63 @@
 			<version>${swagger.version}</version>
 		</dependency>
 
-        <!-- 获取系统信息 -->
+		<!-- 获取系统信息 -->
 		<dependency>
 			<groupId>com.github.oshi</groupId>
 			<artifactId>oshi-core</artifactId>
 			<version>${oshi.version}</version>
 		</dependency>
 
+		<!--文件预览的依赖-->
+		<!--jodconverter 核心包 -->
+		<dependency>
+			<groupId>org.jodconverter</groupId>
+			<artifactId>jodconverter-core</artifactId>
+			<version>4.2.2</version>
+		</dependency>
+
+		<!--springboot支持包,里面包括了自动配置类 -->
+		<dependency>
+			<groupId>org.jodconverter</groupId>
+			<artifactId>jodconverter-spring-boot-starter</artifactId>
+			<version>4.2.2</version>
+		</dependency>
+
+
+		<!--jodconverter 本地支持包 -->
+		<dependency>
+			<groupId>org.jodconverter</groupId>
+			<artifactId>jodconverter-local</artifactId>
+			<version>4.2.2</version>
+		</dependency>
+
+		<!--SVG包-->
+		<dependency>
+			<groupId>org.apache.xmlgraphics</groupId>
+			<artifactId>batik-swing</artifactId>
+			<version>1.13</version>
+		</dependency>
+
+
+
+		<!--视频转码-->
+		<dependency>
+			<groupId>ws.schild</groupId>
+			<artifactId>jave-all-deps</artifactId>
+			<version>3.1.1</version>
+		</dependency>
+
 		<!-- excel工具 -->
 		<dependency>
 			<groupId>org.apache.poi</groupId>
 			<artifactId>poi-ooxml</artifactId>
-<!--			<version>${poi.version}</version>-->
+			<!--			<version>${poi.version}</version>-->
+			<version>4.1.2</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-scratchpad</artifactId>
 			<version>4.1.2</version>
 		</dependency>
 
@@ -243,7 +288,7 @@
 			<version>${velocity.version}</version>
 		</dependency>
 
-        <!-- 定时任务 -->
+		<!-- 定时任务 -->
 		<dependency>
 			<groupId>org.quartz-scheduler</groupId>
 			<artifactId>quartz</artifactId>
@@ -261,10 +306,10 @@
 			<artifactId>kaptcha</artifactId>
 			<version>${kaptcha.version}</version>
 			<exclusions>
-			    <exclusion>
-			        <artifactId>javax.servlet-api</artifactId>
-			        <groupId>javax.servlet</groupId>
-			    </exclusion>
+				<exclusion>
+					<artifactId>javax.servlet-api</artifactId>
+					<groupId>javax.servlet</groupId>
+				</exclusion>
 			</exclusions>
 		</dependency>
 
@@ -398,6 +443,13 @@
 			<artifactId>log4j-to-slf4j</artifactId>
 			<version>${log4j2.version}</version>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.pdfbox</groupId>
+			<artifactId>pdfbox</artifactId>
+			<version>2.0.4</version>
+		</dependency>
+
+
 	</dependencies>
 
 	<build>
@@ -432,6 +484,8 @@
 				<enabled>false</enabled>
 			</snapshots>
 		</repository>
+
+
 	</repositories>
 
 	<pluginRepositories>

+ 45 - 0
master/src/main/java/com/ruoyi/common/utils/ffmpeg/MediaConvertUtils.java

@@ -0,0 +1,45 @@
+package com.ruoyi.common.utils.ffmpeg;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaConvertUtils {
+
+    public static String exchangeToMp4(String ffmpegPath, String upFilePath, String codcFilePath) throws Exception {
+        // 创建List集合来保存转换视频文件为flv格式的命令
+        List<String> convert = new ArrayList<String>();
+        convert.add(ffmpegPath); // 添加转换工具路径
+        convert.add("-y"); // 该参数指定将覆盖已存在的文件
+        convert.add("-i");
+        convert.add(upFilePath);
+        convert.add("-c:v");
+        convert.add("libx264");
+        convert.add("-c:a");
+        convert.add("aac");
+        convert.add("-strict");
+        convert.add("-2");
+        convert.add("-pix_fmt");
+        convert.add("yuv420p");
+        convert.add("-movflags");
+        convert.add("faststart");
+        //convert.add("-vf");   // 添加水印
+        //convert.add("movie=watermark.gif[wm];[in][wm]overlay=20:20[out]");
+        convert.add(codcFilePath);
+
+
+        try {
+            Process videoProcess = new ProcessBuilder(convert).redirectErrorStream(true).start();
+            new PrintStream(videoProcess.getInputStream()).start();
+            //videoProcess.waitFor();  // 加上这句,系统会等待转换完成。不加,就会在服务器后台自行转换。
+
+        } catch (Exception e) {
+
+            System.out.println(e);
+            e.printStackTrace();
+        }
+        return codcFilePath;
+    }
+
+
+
+}

+ 30 - 0
master/src/main/java/com/ruoyi/common/utils/ffmpeg/PrintStream.java

@@ -0,0 +1,30 @@
+package com.ruoyi.common.utils.ffmpeg;
+
+
+public class PrintStream extends Thread
+{
+    //线程
+    java.io.InputStream __is = null;
+    public PrintStream(java.io.InputStream is)
+    {
+        __is = is;
+    }
+
+    public void run()
+    {
+        try
+        {
+            while(this != null)
+            {
+                int _ch = __is.read();
+                if(_ch != -1)
+                    System.out.print((char)_ch);
+                else break;
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}

+ 8 - 3
master/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java

@@ -26,13 +26,18 @@ public class MimeTypeUtils
 
     public static final String[] DEFAULT_ALLOWED_EXTENSION = {
             // 图片
-            "bmp", "gif", "jpg", "jpeg", "png",
+            "bmp", "gif", "jpg", "jpeg", "png","tif",
             // word excel powerpoint
-            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
+            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt","csv","md",
             // 压缩文件
             "rar", "zip", "gz", "bz2",
             // pdf
-            "pdf" };
+            "pdf",
+            //视频
+            "mp4","swf","flv","avi","rm","3gp","mkv",
+            //"图纸"
+            "dwg"
+    };
 
     public static String getExtension(String prefix)
     {

+ 151 - 0
master/src/main/java/com/ruoyi/project/officeConvert/officeConvertController.java

@@ -0,0 +1,151 @@
+package com.ruoyi.project.officeConvert;
+
+import com.google.common.primitives.Bytes;
+import com.ruoyi.common.constant.Constants;
+import com.ruoyi.common.constant.HttpStatus;
+import com.ruoyi.common.utils.ffmpeg.MediaConvertUtils;
+import com.ruoyi.common.utils.file.FileUtils;
+import com.ruoyi.framework.web.domain.AjaxResult;
+
+import org.jodconverter.DocumentConverter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.ruoyi.project.officeConvert.pptPreview.pptToImage;
+import static com.ruoyi.project.officeConvert.pptPreview.pptxToImage;
+
+
+@Component
+@RestController
+@RequestMapping(value="/office",method = RequestMethod.POST)
+public class officeConvertController {
+    /**
+     office类型的文件
+     先远程服务端获取文件
+     然后转换
+     然后读写缓冲区返回前台
+     */
+    // 第一步:转换器直接注入
+    @Resource
+    DocumentConverter documentConverter;
+
+    @Value("${ruoyi.profile}")
+    private String profile;
+
+    private static  String successMsg="请求成功!请预览文件!";
+
+    private static  String failMsg="暂不支持此格式类型文件预览,请下载后查看!";
+
+    private static  String codeMiss="若csv文件预览结果有乱码,请重新上传编码格式另存为ANSI的csv文件!";
+
+    private static  String fileMiss="您需要预览的文件不存在,可能已在本地删除,请重新上传即可!";
+
+    @PostMapping("/toPdfFile")
+    public AjaxResult toPdfFile(HttpServletRequest request, HttpServletResponse response,String filepath) {
+        // 获取HttpServletResponse
+        response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+
+
+        String newFilePath=filepath.replace("/profile","");
+        // 需要转换的文件路径
+        File file = new File(profile+newFilePath);
+        boolean  flag= file.exists();
+        if(flag==false){
+            return new AjaxResult(HttpStatus.SUCCESS,fileMiss,Constants.RESOURCE_PREFIX+ "/" + newFilePath);
+        }
+        String converterPdf ="";  //生成的文件名
+
+        try {
+            // 转换之后文件生成的本地地址
+            File newFile = new File(profile);
+            if (!newFile.exists()) {
+                newFile.mkdirs();
+            }
+            String name =file.getName();
+            int t=name.lastIndexOf(".");
+            String newName=name.substring(0,t);
+            //根据文件类型判断转为pdf还是html,主要是预览效果问题
+            if(newFilePath.endsWith("docx")||newFilePath.endsWith("jpg")||newFilePath.endsWith("png")
+                    ||newFilePath.endsWith(".tif")||newFilePath.endsWith(".rar")){
+                converterPdf =  newName+".pdf";
+            }else if(newFilePath.endsWith(".csv")){
+                converterPdf =  newName+".html";
+
+            }else if(newFilePath.endsWith(".mp4")||newFilePath.endsWith("pdf")||newFilePath.endsWith(".gif")){
+                //直接返回源文件路径
+                return   new AjaxResult(HttpStatus.SUCCESS,successMsg,Constants.RESOURCE_PREFIX+ "/" + newFilePath) ;
+
+            }else if( newFilePath.endsWith("flv")||newFilePath.endsWith("swf")||
+                    newFilePath.endsWith("3gp")||newFilePath.endsWith("mkv")||newFilePath.endsWith(".dwg")) {
+                //直接返回源文件路径
+                return new AjaxResult(HttpStatus.SUCCESS,failMsg,Constants.RESOURCE_PREFIX+ "/" + newFilePath);
+                //原本是将其他格式转换为.mp4
+            /* String codcFilePath=   exchangeToMp4("D:\\ffmpeg\\ffmpeg-2022-06-12-git-4d45f5acbd-essentials_build\\ffmpeg-2022-06-12-git-4d45f5acbd-essentials_build\\bin\\ffmpeg.exe",
+                     profile+newFilePath,profile + "\\"  +converterPdf);
+                documentConverter.convert(new File((codcFilePath))).to(new File(profile + "\\"  +converterPdf)).execute();*/
+            }else {
+                converterPdf =  newName+".html";
+            }
+
+            // 文件转化
+
+            documentConverter.convert(file).to(new File(profile + "\\"  +converterPdf)).execute();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        //最后应该返回转换后的文件名称
+
+        String pathFileName = Constants.RESOURCE_PREFIX+ "/" + converterPdf;
+        return  newFilePath.endsWith(".csv")? new AjaxResult(HttpStatus.SUCCESS,codeMiss,pathFileName): new AjaxResult(HttpStatus.SUCCESS,successMsg,pathFileName) ;
+    }
+
+    @PostMapping("/PPTransJPEG")
+    public AjaxResult PPTransJPEG(HttpServletRequest request, HttpServletResponse response,String filepath) {
+        Map<String, Object> resultMap = new HashMap<>();
+        String newFilePath=filepath.replace("/profile","");
+        // 需要转换的PPT文件路径
+        File file = new File(profile+newFilePath);
+        //判断ppt,pptx文件是否存在本地
+        boolean  flag= file.exists();
+        if(flag==false){
+            return new AjaxResult(HttpStatus.SUCCESS,fileMiss,Constants.RESOURCE_PREFIX+ "/" + newFilePath);
+        }
+
+        //  String imagePath = Constants.RESOURCE_PREFIX+ "/" +   ;
+        File imageFile = new File(profile);
+        if (!imageFile.exists()){
+            imageFile.mkdirs();
+        } //判断生成文件路径是否已存在
+        String name =file.getName();
+        int t=name.lastIndexOf(".");
+        String newName=name.substring(0,t); //将名字带到生成方法中以便于区分不同文件名的ppt
+        List list =new ArrayList();
+        if(newFilePath.endsWith("pptx")){
+            list= pptToImage(file, imageFile,newName);
+        }else {
+            list= pptxToImage(file, imageFile,newName);
+        }
+        //将生成的图片传给前端
+        resultMap.put("imagePathList", list);
+        //  resultMap.put("reviewUrlPrefix",  Constants.RESOURCE_PREFIX+ "/" );
+
+        return  new AjaxResult(HttpStatus.SUCCESS,successMsg,resultMap);
+    }
+
+}

+ 166 - 0
master/src/main/java/com/ruoyi/project/officeConvert/pptPreview.java

@@ -0,0 +1,166 @@
+package com.ruoyi.project.officeConvert;
+
+import com.ruoyi.common.constant.Constants;
+import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.sl.usermodel.TextRun;
+import org.apache.poi.xslf.usermodel.*;
+import org.apache.poi.hslf.usermodel.*;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public class pptPreview {
+
+    //pptx转为图片的方法
+    public static List pptToImage(File pptFile, File imgFile,String newName) {
+        //pptx转为图片 利用轮播图 返回 图片的路径地址
+        List<String> list = new ArrayList<>();
+        FileInputStream is = null ;
+        int imgCount = 0;
+        try {
+            is = new FileInputStream(pptFile);
+            XMLSlideShow xmlSlideShow = new XMLSlideShow(is);
+            is.close();
+            // 获取大小
+            Dimension pgsize = xmlSlideShow.getPageSize();
+            // 获取幻灯片
+            List<XSLFSlide> slides = xmlSlideShow.getSlides();
+            imgCount = slides.size();
+            for (int i = 0 ; i < slides.size() ; i++) {
+                // 解决乱码问题
+                List<XSLFShape> shapes = slides.get(i).getShapes();
+                for (XSLFShape shape : shapes) {
+                    if (shape instanceof XSLFTextShape) {
+                        XSLFTextShape sh = (XSLFTextShape) shape;
+                        List<XSLFTextParagraph> textParagraphs = sh.getTextParagraphs();
+                        for (XSLFTextParagraph xslfTextParagraph : textParagraphs) {
+                            List<XSLFTextRun> textRuns = xslfTextParagraph.getTextRuns();
+                            for (XSLFTextRun xslfTextRun : textRuns) {
+                                xslfTextRun.setFontFamily("宋体");
+                            }
+                        }
+                    }
+                }
+                //根据幻灯片大小生成图片
+
+                BufferedImage img = new BufferedImage(pgsize.width,pgsize.height, BufferedImage.TYPE_INT_RGB);
+                Graphics2D graphics = img.createGraphics();
+                graphics.setPaint(Color.white);
+                graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width,pgsize.height));
+                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+                graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+                graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
+
+
+                //每个文件路径对应自己文件名的文件夹,以区别防止文件名也重复
+                // 最核心的代码
+                slides.get(i).draw(graphics);
+                //图片将要存放的路径
+                String absolutePath = imgFile.getAbsolutePath()+"/"+ newName+(i + 1) + ".jpeg";
+                File jpegFile = new File(absolutePath);
+                // 图片路径存放
+                list.add(Constants.RESOURCE_PREFIX+ "/"+newName+(i + 1) + ".jpeg");
+                /*//如果图片存在,则不再生成
+                if (jpegFile.exists()) {
+                    continue;
+                }*/
+                // 这里设置图片的存放路径和图片的格式(jpeg,png,bmp等等),注意生成文件路径
+                FileOutputStream out = new FileOutputStream(jpegFile);
+                // 写入到图片中去
+                ImageIO.write(img, "jpeg", out);
+                out.close();
+            }
+            System.out.print("PPT转换成图片 成功!");
+            //log.error("PPT转换成图片 成功!");
+            return list;
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            System.out.print("PPT转换成图片 发生异常!");
+            // log.error("PPT转换成图片 发生异常!", e);
+        }
+        return list;
+    }
+
+    //ppt和pptx仅用的操作类不一样,其他逻辑一致
+    public static List pptxToImage(File pptFile, File imgFile,String newName) {
+        //ppt转为图片 利用轮播图 返回 图片的路径地址
+        List<String> list = new ArrayList<>();
+        FileInputStream is = null ;
+        int imgCount = 0;
+        try {
+            is = new FileInputStream(pptFile);
+            //使用hslf即可
+            HSLFSlideShow ppt = new HSLFSlideShow(is);
+            //及时关闭掉 输入流
+            is.close();
+            Dimension pgsize = ppt.getPageSize();
+            List<HSLFSlide> slide = ppt.getSlides();
+            for (int i = 0; i < slide.size(); i++) {
+
+                List<HSLFShape> shapes = slide.get(i).getShapes();
+                for (HSLFShape shape : shapes) {
+                    if (shape instanceof HSLFTextShape) {
+                        HSLFTextShape sh = (HSLFTextShape) shape;
+                        List<HSLFTextParagraph> textParagraphs = sh.getTextParagraphs();
+                        for (HSLFTextParagraph xslfTextParagraph : textParagraphs) {
+                            List<HSLFTextRun> textRuns = xslfTextParagraph.getTextRuns();
+                            for (HSLFTextRun xslfTextRun : textRuns) {
+                                xslfTextRun.setFontFamily("宋体");
+                            }
+                        }
+                    }
+                }
+                //根据幻灯片大小生成图片
+
+                BufferedImage img = new BufferedImage(pgsize.width,pgsize.height, BufferedImage.TYPE_INT_RGB);
+                Graphics2D graphics = img.createGraphics();
+                graphics.setPaint(Color.white);
+                graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width,pgsize.height));
+                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+                graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+                graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+                graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+                graphics.setRenderingHint(Drawable.BUFFERED_IMAGE, new WeakReference<>(img));
+
+
+                //每个文件路径对应自己文件名的文件夹,以区别防止文件名也重复
+                // 最核心的代码
+                slide.get(i).draw(graphics);
+                //图片将要存放的路径
+                String absolutePath = imgFile.getAbsolutePath()+"/"+ newName+(i + 1) + ".jpeg";
+                File jpegFile = new File(absolutePath);
+                // 图片路径存放
+                list.add(Constants.RESOURCE_PREFIX+ "/"+newName+(i + 1) + ".jpeg");
+                /*//如果图片存在,则不再生成
+                if (jpegFile.exists()) {
+                    continue;
+                }*/
+                // 这里设置图片的存放路径和图片的格式(jpeg,png,bmp等等),注意生成文件路径
+                FileOutputStream out = new FileOutputStream(jpegFile);
+                // 写入到图片中去
+                ImageIO.write(img, "jpeg", out);
+                out.close();
+            }
+            System.out.print("PPT转换成图片 成功!");
+            //log.error("PPT转换成图片 成功!");
+            return list;
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            System.out.print("PPT转换成图片 发生异常!");
+            // log.error("PPT转换成图片 发生异常!", e);
+        }
+        return list;
+    }
+}

+ 10 - 0
master/src/main/resources/application.yml

@@ -170,6 +170,14 @@ xss:
   excludes: /system/notice/*
   # 匹配链接
   urlPatterns: /system/*,/monitor/*,/tool/*
+# 文件预览
+jodconverter:
+  local:
+    enabled: true
+    max-tasks-per-process: 100
+    port-numbers: 8100
+    # 开启多个LibreOffice进程,每个端口对应一个进程
+    portNumbers: 9080,9081,9089
 
 # 代码生成
 gen:
@@ -186,3 +194,5 @@ gen:
 jasypt:
   encryptor:
     password: test
+
+

+ 0 - 3
package-lock.json

@@ -1,3 +0,0 @@
-{
-  "lockfileVersion": 1
-}

+ 3 - 0
ui/package.json

@@ -52,6 +52,7 @@
     "element-ui": "^2.15.5",
     "file-saver": "2.0.1",
     "fuse.js": "3.4.4",
+    "jquery": "^3.6.0",
     "js-beautify": "1.10.2",
     "js-cookie": "2.2.0",
     "jsencrypt": "3.0.0-rc.1",
@@ -82,6 +83,7 @@
     "babel-eslint": "10.1.0",
     "babel-jest": "23.6.0",
     "babel-plugin-dynamic-import-node": "2.3.3",
+    "bootstrap": "^5.1.3",
     "chalk": "2.4.2",
     "chokidar": "2.1.5",
     "connect": "3.6.6",
@@ -89,6 +91,7 @@
     "eslint-plugin-vue": "6.2.2",
     "html-webpack-plugin": "3.2.0",
     "husky": "1.3.1",
+    "jquery": "^3.6.0",
     "lint-staged": "8.1.5",
     "mockjs": "1.0.1-beta3",
     "node-sass": "4.14.1",

+ 18 - 0
ui/src/api/system/dept.js

@@ -74,3 +74,21 @@ export function delDept(deptId) {
     method: 'delete'
   })
 }
+
+// 文件预览 参数为文件的虚拟路径
+export function officeConvert(data) {
+  return request({
+    url: '/office/toPdfFile',
+    method: 'post',
+    data: data
+  })
+}
+
+// 文件预览 参数为文件的虚拟路径
+export function pptConvert(data) {
+  return request({
+    url: '/office/PPTransJPEG',
+    method: 'post',
+    data: data
+  })
+}

+ 37 - 0
ui/src/assets/css/myCss.css

@@ -0,0 +1,37 @@
+.el-loading-spinner{
+
+    /*这个是自己想设置的 gif 加载动图*/
+  
+    background-image:url('../img/loading.gif');
+  
+    background-repeat: no-repeat;
+  
+    background-size: 200px 120px;
+  
+    height:100px;
+  
+    width:100%;
+  
+    background-position:center;
+  
+    /*覆盖 element-ui 默认的 50% 因为此处设置了height:100%,所以不设置的话,会只显示一半,因为被top顶下去了*/
+  
+    top:40%;
+  
+   }
+  
+  .el-loading-spinner .circular {
+  
+   /*隐藏 之前 element-ui 默认的 loading 动画*/
+  
+   display: none; 
+  
+  } 
+  
+  .el-loading-spinner .el-loading-text{
+  
+   /*为了使得文字在loading图下面*/
+  
+   margin:85px 0px; 
+  
+  }

BIN
ui/src/assets/img/loading.gif


+ 10 - 1
ui/src/main.js

@@ -1,14 +1,19 @@
 import Vue from 'vue'
-
+import $ from 'jquery' ;
+import 'bootstrap'
 import Cookies from 'js-cookie'
 
 import 'normalize.css/normalize.css' // a modern alternative to CSS resets
 
 import Element from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css';
 import './assets/styles/element-variables.scss'
+import './assets/css/myCss.css';
 
 import '@/assets/styles/index.scss' // global css
 import '@/assets/styles/ruoyi.scss' // ruoyi css
+  
+
 import App from './App'
 import store from './store'
 import router from './router'
@@ -27,6 +32,8 @@ import dataV from '@jiaminghi/data-view';
 import RightToolbar from "@/components/RightToolbar"
 //echarts
 import echarts from '@/api/cpms/echarts.min.js'
+import officeConvert from '@/utils/officeConvert.js'  //全局预览方法
+//import { Loading } from 'element-ui'; //全局loading
 // 按需引入vue-awesome图标
 import Icon from 'vue-awesome/components/Icon';
 import 'vue-awesome/icons/chart-bar.js';
@@ -52,6 +59,8 @@ Vue.prototype.selectDictLabels = selectDictLabels
 Vue.prototype.download = download
 Vue.prototype.handleTree = handleTree
 Vue.prototype.echarts = echarts
+Vue.prototype.officeConvert = officeConvert
+
 
 Vue.prototype.msgSuccess = function (msg) {
   this.$message({ showClose: true, message: msg, type: "success" });

+ 6 - 0
ui/src/router/index.js

@@ -61,6 +61,12 @@ export const constantRoutes = [
     component: (resolve) => require(['@/views/monitor/elec/index'], resolve),
     hidden: true
   },
+  {
+    path: '/pptyulan',
+    name:'pptyulan',
+    component: (resolve) => require(['@/views/yulan/index'], resolve),
+    hidden: true
+  },
   {
     path: '/elecDashboard',
     component: (resolve) => require(['@/views/monitor/elec/elecindex'], resolve),

+ 28 - 0
ui/src/utils/officeConvert.js

@@ -0,0 +1,28 @@
+import {officeConvert,pptConvert} from "@/api/system/dept";
+export default {
+ 
+       //通用的文件预览查看全局方法
+       officeConvertCommon: function (data) {
+
+    return    officeConvert (data).then(response => {
+
+   //     console.log(response) 接口返回的数据
+            return response
+
+
+          })
+
+   },
+
+     pptConvertCommon: function (data) {
+
+      return    pptConvert (data).then(response => {
+  
+     //     console.log(response) 接口返回的数据
+              return response
+  
+  
+            })
+  
+     }
+}

+ 205 - 66
ui/src/views/components/PlantProgList/index.vue

@@ -55,15 +55,15 @@
           v-hasPermi="['document:plantproglist:edit']"
         >{{ $t('导入') }}</el-button>
       </el-col>
-<!--      <el-col :span="1.5">-->
-<!--        <el-button-->
-<!--          type="warning"-->
-<!--          icon="el-icon-download"-->
-<!--          size="mini"-->
-<!--          @click="handleExport"-->
-<!--          v-hasPermi="['document:plantproglist:export']"-->
-<!--        >{{ $t('导出') }}</el-button>-->
-<!--      </el-col>-->
+      <!--      <el-col :span="1.5">-->
+      <!--        <el-button-->
+      <!--          type="warning"-->
+      <!--          icon="el-icon-download"-->
+      <!--          size="mini"-->
+      <!--          @click="handleExport"-->
+      <!--          v-hasPermi="['document:plantproglist:export']"-->
+      <!--        >{{ $t('导出') }}</el-button>-->
+      <!--      </el-col>-->
       <el-col :span="1.5">
         <el-button
           type="warning"
@@ -374,50 +374,72 @@
           <em>{{ $t('点击上传') }}</em>
         </div>
       </el-upload>
-        <el-table :data="doc.commonfileList" border>
-          <el-table-column :label="$t('文件名')" align="center" prop="fileName" :show-overflow-tooltip="true">
+      <el-table :data="doc.commonfileList" border>
+        <el-table-column :label="$t('文件名')" align="center" prop="fileName" :show-overflow-tooltip="true">
           <template slot-scope="scope">
             <a  class="link-type"  @click="handleDownload(scope.row)">
               <span>{{ scope.row.fileName }}</span>
             </a>
           </template>
-          </el-table-column>
-          <el-table-column :label="$t('大小(Kb)')" align="center" prop="fileSize" :show-overflow-tooltip="true" width="80" />
-          <el-table-column :label="$t('上传人')" align="center" prop="creator" :show-overflow-tooltip="true" width="120"/>
-          <el-table-column :label="$t('操作')" align="center" width="150" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button
-                v-if="scope.row.fileName.endsWith('pdf')"
-                size="mini"
-                type="text"
-                icon="el-icon-view"
-                @click="handleSee(scope.row)"
-              >{{ $t('预览') }}</el-button>
-              <el-button
-                size="mini"
-                type="text"
-                icon="el-icon-download"
-                @click="handleDownload(scope.row)"
-              >{{ $t('下载') }}</el-button>
-              <el-button
-                size="mini"
-                type="text"
-                icon="el-icon-delete"
-                @click="handleDeleteDoc(scope.row)"
-              >{{ $t('删除') }}</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
+        </el-table-column>
+        <el-table-column :label="$t('大小(Kb)')" align="center" prop="fileSize" :show-overflow-tooltip="true" width="80" />
+        <el-table-column :label="$t('上传人')" align="center" prop="creator" :show-overflow-tooltip="true" width="120"/>
+        <el-table-column :label="$t('操作')" align="center" width="150" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-button
+              v-if="scope.row.fileName.endsWith('pdf')||scope.row.fileName.endsWith('xlsx')||scope.row.fileName.endsWith('md')
+                 ||scope.row.fileName.endsWith('docx')||scope.row.fileName.endsWith('doc')||scope.row.fileName.endsWith('txt')
+                 ||scope.row.fileName.endsWith('jpg')||scope.row.fileName.endsWith('png')||scope.row.fileName.endsWith('csv')
+                 ||scope.row.fileName.endsWith('mp4')||scope.row.fileName.endsWith('svg')||scope.row.fileName.endsWith('dwg')
+                 ||scope.row.fileName.endsWith('flv')||scope.row.fileName.endsWith('swf')||scope.row.fileName.endsWith('gif')
+                 ||scope.row.fileName.endsWith('3gp')||scope.row.fileName.endsWith('mkv')||scope.row.fileName.endsWith('tif')"
+              size="mini"
+              type="text"
+              icon="el-icon-view"
+              @click="handleSee(scope.row)"
+            > {{ $t('预览') }}</el-button>
+            <el-button
+              v-if="scope.row.fileName.endsWith('ppt')||scope.row.fileName.endsWith('pptx') "
+              size="mini"
+              type="text"
+              icon="el-icon-view"
+              @click="handleSeePPT(scope.row)"
+            > {{ $t('ppt预览') }}</el-button>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-download"
+              @click="handleDownload(scope.row)"
+            >{{ $t('下载') }}</el-button>
+            <el-button
+              size="mini"
+              type="text"
+              icon="el-icon-delete"
+              @click="handleDeleteDoc(scope.row)"
+            >{{ $t('删除') }}</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
       <div slot="footer" class="dialog-footer">
-<!--        <el-button type="primary" @click="submitFileForm">{{ $t('确 定') }}</el-button>-->
+        <!--        <el-button type="primary" @click="submitFileForm">{{ $t('确 定') }}</el-button>-->
         <el-button @click="doc.open = false">{{ $t('返 回') }}</el-button>
       </div>
     </el-dialog>
-    <el-dialog v-dialogDrag :title="pdf.title" :visible.sync="pdf.open" width="1300px" append-to-body>
-        <div style="margin-top: -60px;float: right;margin-right: 40px;">
-          <el-button size="mini" type="text" @click="openPdf">{{$t('新页面打开PDF')}}</el-button></div>
-      <div style="margin-top: -30px">
-        <iframe :src="pdf.pdfUrl" frameborder="0" width="100%" height="700px"></iframe>
+    <el-dialog  v-loading="loadingFlash"     element-loading-background="rgba(0,0,0,0.2)"
+                v-dialogDrag :title="pdf.title" :visible.sync="pdf.open"  width="1300px" append-to-body>
+      <div style="margin-top: -60px;float: right;margin-right: 40px;">
+        <el-button size="mini" type="text" @click="openPdf">{{$t('新页面打开PDF')}}</el-button></div>
+      <div style="margin-top: -30px" >
+        <iframe :src="pdf.pdfUrl" frameborder="0" width="100%" height="700px" v-if="ppt"></iframe>
+      </div>
+      <div style="margin-left:10%" >
+        <el-carousel class="" ref="carousel"  arrow="always"  v-if="pptView"
+                     height="700px"  trigger="click" :autoplay="false" indicator-position="outside">
+          <el-carousel-item class="lun_img" v-for="item in imgs" v-bind:key="item" >
+            <img :src="item" width="100%" height="100%" object-fit="cover" />
+          </el-carousel-item>
+        </el-carousel>
+
       </div>
     </el-dialog>
     <el-drawer
@@ -454,35 +476,40 @@
 </template>
 
 <script>
-  import {
-    addPlantproglist,
-    delPlantproglist,
-    exportPlantproglist,
-    getPlantproglist,
-    listPlantproglist,
-    updatePlantproglist
-  } from "@/api/document/plantproglist";
-  import {allFileList, delCommonfile} from "@/api/common/commonfile";
-  import {treeselect} from "@/api/system/dept";
-  import {getToken} from "@/utils/auth";
-  import Treeselect from "@riophae/vue-treeselect";
-  import "@riophae/vue-treeselect/dist/vue-treeselect.css";
-  import RcData from "./rcData";
-  import ClassifyData from "./classifyData";
-
-  import {formatDate} from "../../../utils";
-
-  export default {
+import {
+  addPlantproglist,
+  delPlantproglist,
+  exportPlantproglist,
+  getPlantproglist,
+  listPlantproglist,
+  updatePlantproglist
+} from "@/api/document/plantproglist";
+import {allFileList, delCommonfile} from "@/api/common/commonfile";
+import {treeselect} from "@/api/system/dept";
+import {getToken} from "@/utils/auth";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import RcData from "./rcData";
+import ClassifyData from "./classifyData";
+
+import {formatDate} from "../../../utils";
+
+export default {
   name: "index",
   props: ['item', 'typename'],
   components: {ClassifyData, RcData, Treeselect},
   data() {
     return {
+      imgs:[],
+      jpgList:[],
+      ppt:false,
+      pptView:false,
       drawer: false,
       direction: 'rtl',
       sonRefresh: true,
       // 遮罩层
       loading: true,
+      loadingFlash: false,
       // 选中数组
       ids: [],
       // 非单个禁用
@@ -649,11 +676,13 @@
     this.$nextTick(() => {
       this.clientHeight = (document.body.clientHeight - 80) * 0.8
     })
+
     this.getList();
     this.getTreeselect();
     this.getDicts("CLASSIFY").then(response => {
       this.classifyOptions = response.data;
     });
+
   },
   methods: {
     /** 查询装置程序清单列表 */
@@ -918,15 +947,125 @@
       });
       this.drawer = true
     },
-    //pdf预览
+    //文件预览
     openPdf(){
-      window.open(this.pdf.pdfUrl);//path是文件的全路径地址
+      //ppt就跳路由预览,office就直接打开文件新页面
+      const didi={ imgs:this.imgs}
+      if( this.pptView==true&&this.ppt==false){
+        let routeUrl = this.$router.resolve({
+          path: "/pptyulan",
+          query:didi
+
+        });
+        window.open(routeUrl.href, '_blank');
+        console.log(this.imgs)
+      }else {
+        window.open(this.pdf.pdfUrl);
+      }
+    },
+    handleSeePPT (row){
+      //ppt预览
+      this.loadingFlash=true
+      this.pdf.open =true
+      this.pdf.title = row.fileName
+      this.pdf.pdfUrl =""
+      this.pptView=true
+      this.ppt=false
+      const formatDate =new FormData();
+      formatDate.append("filepath",row.fileUrl)
+
+      //调用文件预览api
+      let res= this.officeConvert.pptConvertCommon(formatDate)
+
+      //查看接受的全局方法的返回结果 console.log(res)
+      //利用.then方法接受Promise对象
+
+      res.then((result)=>{
+        //关闭加载中
+        this.loadingFlash=false
+
+        //成功时直接给地址
+
+        this.videoList = result.data.imagePathList
+        //将返回的地址集合遍历添加到绑定的数组中
+        this.imgs=[]
+        for (var key=0;key<this.videoList.length;key++) {
+          this.imgs.push( process.env.VUE_APP_BASE_API+  this.videoList[key]  );
+
+        }
+
+
+      }).catch(result => {
+
+
+        //请求失败,关闭loading,pdf地址直接为为空,不显示
+        this.pdf.pdfUrl =""
+        this.loadingFlash = false;
+
+      })
     },
     handleSee (row){
+      //office预览
+      this.loadingFlash=true
       this.pdf.open =true
       this.pdf.title = row.fileName
-      this.pdf.pdfUrl = process.env.VUE_APP_BASE_API +'/pdf/web/viewer.html?file=' + process.env.VUE_APP_BASE_API + row.fileUrl
+      this.pdf.pdfUrl =""
+
+      this.pptView=false
+      this.ppt=true
+
+      const formatDate =new FormData();
+      formatDate.append("filepath",row.fileUrl)
+
+      //调用文件预览api
+      let res= this.officeConvert.officeConvertCommon(formatDate)
+
+
+      //查看接受的全局方法的返回结果 console.log(res)
+      //利用.then方法接受Promise对象
+      res.then((result)=>{
+        //关闭加载中
+        this.loadingFlash=false
+
+        if(result.msg.includes("csv")){
+          this.pdf.pdfUrl =process.env.VUE_APP_BASE_API+ result.data
+          this.$alert(result.msg, this.$t('检查乱码'), { dangerouslyUseHTMLString: true });
+          //    this.$message({message: result.msg, center: true,type:'warning',  offset:400, });
+
+        }else if(result.msg.includes("不存在")){
+          //文件不存在时提示
+          this.pdf.pdfUrl =""
+          this.$alert(result.msg, this.$t('预览失败'), { dangerouslyUseHTMLString: true });
+          //    this.$message({message: result.msg, center: true,type:'warning',  offset:400, });
+          this.pdf.open =false
+        }else if(result.msg.includes("不支持此格式")){
+
+          this.pdf.pdfUrl =""
+          this.$alert(result.msg, this.$t('预览失败'), { dangerouslyUseHTMLString: true });
+          //    this.$message({message: result.msg, center: true,type:'warning',  offset:400, });
+          this.pdf.open =false
+        } else{
+          //成功时直接给地址
+
+          this.pdf.pdfUrl =process.env.VUE_APP_BASE_API+ result.data
+
+        }
+
+
+      }).catch(result => {
+
+
+        //请求失败,关闭loading,pdf地址直接为为空,不显示
+        this.pdf.pdfUrl =""
+        this.loadingFlash = false;
+
+      })
+
+
+
     },
+
   }
 }
 </script>
+

+ 52 - 0
ui/src/views/yulan/index.vue

@@ -0,0 +1,52 @@
+<template>
+
+
+  <div style="" >
+    <el-carousel class="" ref="carousel"  arrow="always"  v-if="pptView"
+                 height="900px"  trigger="click" :autoplay="false" indicator-position="outside">
+
+      <el-carousel-item class="lun_img" v-for="(item,index) in imgs" v-bind:key="item" >
+        <img :src="item" width="100%" height="100%" object-fit="cover" />
+      </el-carousel-item>
+    </el-carousel>
+
+  </div>
+
+</template>
+<script>
+
+export default {
+
+  data() {
+    return {
+      imgs:[],
+      pptView:false,
+    }
+  },
+  mounted() {
+    this.ppt()
+  },
+
+
+  methods: {
+    ppt(){
+
+      this.pptView=true;
+
+      console.log(this.$route.query.imgs)
+      //判断ppt是否可能出现只有一页而页面加载报错的情况 一页就返回string 多页就是返回集合
+      if(typeof this.$route.query.imgs =='string'){
+
+        this.imgs.push( this.$route.query.imgs);
+
+      }else {
+        this.imgs=this.$route.query.imgs
+
+      }
+      console.log(this.imgs)
+
+
+    }
+  }
+}
+</script>

+ 11 - 6
ui/vue.config.js

@@ -1,6 +1,7 @@
 'use strict'
 const path = require('path')
 const defaultSettings = require('./src/settings.js')
+const webpack = require("webpack");
 
 function resolve(dir) {
   return path.join(__dirname, dir)
@@ -27,8 +28,10 @@ module.exports = {
   lintOnSave: process.env.NODE_ENV === 'development',
   // 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
   productionSourceMap: false,
+
   // webpack-dev-server 相关配置
   devServer: {
+
     host: '0.0.0.0',
     port: port,
     open: true,
@@ -45,12 +48,14 @@ module.exports = {
     disableHostCheck: true
   },
   configureWebpack: {
-    name: name,
-    resolve: {
-      alias: {
-        '@': resolve('src')
-      }
-    }
+    plugins: [
+      new webpack.ProvidePlugin({
+        $: "jquery",
+        jQuery: "jquery",
+        jquery: "jquery",
+        "window.jQuery": "jquery"
+      })
+    ],
   },
   chainWebpack(config) {
     //去除打包后缀的hash值