springboot搭建文件预览解决方案,支持目前主流格式office文件,txt文件,png,jpg等图片以及压缩文件的在线预览功能

2023-11-15

 

应用场景及实现思路

应用场景:给定一个网址,输入网址后立即显示预览文件。

实现思路:
1、将文件下载到本地,存储到某个指定目录 
2、进行文件转换,此处是重点     
3、进行文件展示

实现过程

首先,搭建一个springboot项目,搭建完毕后项目结构如图:

目录结构中static存放的是静态资源,templates中存放页面。其中配置文件里存放我们的配置信息:application.yml

其中tmp:root下面是转换后文件的存放位置,rootTemp则是下载文件的临时存放位置,后续会有定时器定时删除该目录下的内容,soffice:home配置的是openoffice的安装目录,因为office文件的转换要用到openoffice。其中的type则是可以预览的文件类型。好了,搭建完毕之后开始搭建Service层,Dao层(可用可不用),Controller层。整体搭建完毕之后如下图:

相关依赖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">
	<modelVersion>4.0.0</modelVersion>

	<name>file-conventer</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- jchardet检查文本字符编码 start -->
		<dependency>
			<groupId>net.sourceforge.jchardet</groupId>
			<artifactId>jchardet</artifactId>
			<version>1.0</version>
		</dependency>
		<!-- jchardet检查文本字符编码 end -->
		<dependency>
			<groupId>org.artofsolving.jodconverter</groupId>
			<artifactId>jodconverter-core</artifactId>
			<version>3.0.0</version>
		</dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-compress</artifactId>
            <version>1.11</version>
        </dependency>
		<!-- 解压(apache) -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-compress</artifactId>
			<version>1.9</version>
		</dependency>
		<!-- 解压(rar)-->
		<dependency>
			<groupId>com.github.junrar</groupId>
			<artifactId>junrar</artifactId>
			<version>0.7</version>
		</dependency>
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>19.0</version>
		</dependency>
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>2.0-SNAPSHOT</version>
		</dependency>
		<!--解压(zip4j) -->
		<dependency>
			<groupId>net.lingala.zip4j</groupId>
			<artifactId>zip4j</artifactId>
			<version>1.3.2</version>
		</dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.10.RELEASE</version>
        </dependency>
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>2.3.25-incubating</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
		<dependency>
			<groupId>net.sf.json-lib</groupId>
			<artifactId>json-lib</artifactId>
			<version>2.4</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.3.1</version>
		</dependency>
		<!-- openoffice 相关依赖 -->
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>1.4</version>
		</dependency>
		<dependency>
			<groupId>org.openoffice</groupId>
			<artifactId>juh</artifactId>
			<version>3.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.openoffice</groupId>
			<artifactId>ridl</artifactId>
			<version>3.2.1</version>
		</dependency>
		<dependency>
			<groupId>org.openoffice</groupId>
			<artifactId>unoil</artifactId>
			<version>3.2.1</version>
		</dependency>
		<dependency>
			<!-- for the command line tool -->
			<groupId>commons-cli</groupId>
			<artifactId>commons-cli</artifactId>
			<version>1.1</version>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.hyperic</groupId>
			<artifactId>sigar</artifactId>
			<version>1.6.5.132</version>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.json</groupId>
			<artifactId>json</artifactId>
			<version>20090211</version>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.testng</groupId>
			<artifactId>testng</artifactId>
			<version>6.0.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

项目开始,要做的第一件事是下载文件,这里下载文件有一个要注意的地方

我没有截全,因为文件的下载在这篇文章里不是重点,所以在这里不做详细记录,只要最后将文件存储到临时目录就行

文件下载完之后在进行文章的转换,这里我的转换规则:
1、图片不进行转换
2、文本文件转换编码为utf-8
3、office文件选择将word文档转为pdf文件,xls表格和ppt文件转为html文件,因为表格和ppt文件转换为pdf格式之后不太美观,所以选择将ppt转为html格式,后期取图片展示就可以了
4、压缩文件先解压到本地,然后运用Ztree插件做前端的展示。

由于涉及文件的操作,所以在这里一定要注意文件流的运用,用完了一定要及时关闭,还有文件的其他操作也要注意,比如文件地址的指向。当时我就是因为一个文件地址未仍指向一个操作中的目录导致后面临时文件的删除出错。

下面开始文件的转换,重点记录文本文件以及压缩文件的转换,当时在这花了较长时间,office文件的转换可以参照其他人写的。

文本文件的转换(这里要注意的是文本文件涉及到文件的编码问题,这里采用文本编码探测器进行探测):

 

文本探测器如下:

import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;

import java.io.*;

/**
 * Created by asus on 2017/12/28.
 */
public class FileCharsetDetector {

    /**
     * 传入一个文件(File)对象,检查文件编码
     *
     * @param file
     *            File对象实例
     * @return 文件编码,若无,则返回null
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static Observer guessFileEncoding(File file)
            throws FileNotFoundException, IOException {
        return guessFileEncoding(file, new nsDetector());
    }

    /**
     * <pre>
     * 获取文件的编码
     * @param file
     *            File对象实例
     * @param languageHint
     *            语言提示区域代码 @see #nsPSMDetector ,取值如下:
     *             1 : Japanese
     *             2 : Chinese
     *             3 : Simplified Chinese
     *             4 : Traditional Chinese
     *             5 : Korean
     *             6 : Dont know(default)
     * </pre>
     *
     * @return 文件编码,eg:UTF-8,GBK,GB2312形式(不确定的时候,返回可能的字符编码序列);若无,则返回null
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static Observer guessFileEncoding(File file, int languageHint)
            throws FileNotFoundException, IOException {
        return guessFileEncoding(file, new nsDetector(languageHint));
    }

    /**
     * 获取文件的编码
     *
     * @param file
     * @param det
     * @return
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static Observer guessFileEncoding(File file, nsDetector det)
            throws FileNotFoundException, IOException {
        // new Observer
        Observer observer = new Observer();
        // set Observer
        // The Notify() will be called when a matching charset is found.
        det.Init(observer);

        BufferedInputStream imp = new BufferedInputStream(new FileInputStream(
                file));
        byte[] buf = new byte[1024];
        int len;
        boolean done = false;
        boolean isAscii = false;

        while ((len = imp.read(buf, 0, buf.length)) != -1) {
            // Check if the stream is only ascii.
            isAscii = det.isAscii(buf, len);
            if (isAscii) {
                break;
            }
            // DoIt if non-ascii and not done yet.
            done = det.DoIt(buf, len, false);
            if (done) {
                break;
            }
        }
        imp.close();
        det.DataEnd();

        if (isAscii) {
            observer.encoding = "ASCII";
            observer.found = true;
        }

        if (!observer.isFound()) {
            String[] prob = det.getProbableCharsets();
            // // 这里将可能的字符集组合起来返回
            // for (int i = 0; i < prob.length; i++) {
            // if (i == 0) {
            // encoding = prob[i];
            // } else {
            // encoding += "," + prob[i];
            // }
            // }
            if (prob.length > 0) {
                // 在没有发现情况下,去第一个可能的编码
                observer.encoding = prob[0];
            } else {
                return null;
            }
        }
        return observer;
    }

    /**
     * @Description: 文件字符编码观察者,但判断出字符编码时候调用
     */
    public static class Observer implements nsICharsetDetectionObserver {

        /**
         * @Fields encoding : 字符编码
         */
        private String encoding = null;
        /**
         * @Fields found : 是否找到字符集
         */
        private boolean found = false;

        @Override
        public void Notify(String charset) {
            this.encoding = charset;
            this.found = true;
        }

        public String getEncoding() {
            return encoding;
        }

        public boolean isFound() {
            return found;
        }

        @Override
        public String toString() {
            return "Observer [encoding=" + encoding + ", found=" + found + "]";
        }
    }

}

压缩文件转换(因为前端要生成文件树,所以在这里要先进行文件解压,在进行文件的读取,最终要生成的是一段字符串里面包含所有文件的信息):

解压文件的操作这里也不详细记录了,很多网上的资料,这里记录一下文件树的生成,首先我们定义一个文件节点,里面包含子文件,文件名称,判断是否为文件夹以及文件绝对路径:

/**
     * 文件节点(区分文件上下级)
     */
    public static class FileNode{

        private String originName;
        private boolean directory;
        private String fullPath;

        private List<FileNode> childList;

        public FileNode(String originName, List<FileNode> childList, boolean directory, String fullPath) {
            this.originName = originName;
            this.childList = childList;
            this.directory = directory;
            this.fullPath = fullPath;

        }

        public String getFullPath() {
            return fullPath;
        }

        public void setFullPath(String fullPath) {
            this.fullPath = fullPath;
        }

        public List<FileNode> getChildList() {
            return childList;
        }

        public void setChildList(List<FileNode> childList) {
            this.childList = childList;
        }

        @Override
        public String toString() {
            try {
                return new ObjectMapper().writeValueAsString(this);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
                return "";
            }
        }

        public String getOriginName() {
            return originName;
        }

        public void setOriginName(String originName) {
            this.originName = originName;
        }

        public boolean isDirectory() {
            return directory;
        }

        public void setDirectory(boolean directory) {
            this.directory = directory;
        }
    }

 
/**
     * 通过递归得到某一路径下所有的目录及其文件
     */
    public static List<FileNode> getFiles(String filePath){
        File root = new File(filePath);
        File[] files = root.listFiles();
        String originName = "";
        boolean isDirectory = false;
        String fullPath = "";

        List<FileNode> fileNodes = new ArrayList<>();
        for(File file:files){

            List<FileNode> childList = new ArrayList<>();

            if(file.isDirectory()){
                isDirectory = true;
                originName = file.getName();
                fullPath = file.getAbsolutePath();
                childList = getFiles(file.getPath());
            } else {
                originName = file.getName();
                isDirectory = false;
                fullPath = file.getAbsolutePath();
            }
            // 进行转义,否则json解析不了
            fullPath = fullPath.replace("\\", "/");
            FileNode fileNode = new FileNode(originName, childList, isDirectory, fullPath);
            fileNodes.add(fileNode);
        }
        return fileNodes;
    }

这样我们就得到了文件树,前端的展示就简单多了,后面只需要把文件树转为字符串传到前端j就行了,注意这里的文件绝对路径的写法,默认是生成“\\”,这里要换成“/”。到这步,文件的转换基本就差不多了,剩下我们要做的就是文件的展示。这里还有一个地方需要注意的是,生成文件存储目录的时候增加一层目录用于区分唯一文件,这里我采用的是取文件的hash值,作为文件的上级存储目录,这样就不会有重复的文件目录了。


文件展示 :前面一直没有说Controller层如何写,这里开始说明

 

 

 

 

原理就是转换完文件之后再定位到文件存放的目录,将本地文件以流的方式输出到页面。

这样写完之后便可以写页面了,页面显示规则:1、图片的显示可以用viewer.js插件(一个图片显示器,支持主流的图片显示操作),我这里直接写到页面上,后续再加上。2、pdf文件的显示可以用pdf.js插件。3、压缩文件显示用Ztree插件。4、ppt文件的显示这里推荐一个开源js,个人觉得还不错,点击打开链接

最后,显示效果如下:

压缩文件:
文档:

报表:

PPT文件:

图片:

总结

个人项目可以采用和我一样的方式,写的有点乱,有问题欢迎在底部留言。最后附上项目地址:https://github.com/Chenchicheng/file_viewer

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

springboot搭建文件预览解决方案,支持目前主流格式office文件,txt文件,png,jpg等图片以及压缩文件的在线预览功能 的相关文章

随机推荐

  • adb将Apk内置到系统中(system/priv-app)

    有时候我们在Android 系统内置自己的应用 在测试时 Android Studio 默认的安装方式是将我们开发的应用作为普通应用安装到系统中的 本文提供一种方式 在开发过程中 将apk内置到系统中 而不需要系统源代码 adb 将apk内
  • java新建一个窗口_Java实例:使用JFram创建一个简单的窗口

    一个图形用户界面以一个top level container开始 它为其他的界面组件提供了一个 家 指定应用程序的总体感觉 在本教程中 向你介绍JFrame类 它将用于给一个Java应用程序创建一个简单的top level窗口 打开你的编辑
  • UI操作 解决方案

    1 include
  • 中断、信号、系统调用

    1 中断的分类 中断程序的方法可以分为硬件中断和软件中断 硬件中断是硬件自动触发的 包括中断和异常 比如 中断 通过中断控制器给CPU的INTR引脚发送信号 如按下键盘 会给CPU一个0x21中断号 异常 CPU执行某条指令发生异常 会自己
  • tr td分合并单元格

    table border 1 width 200 tr td ss td tr tr td width 25 td td width 25 td tr table
  • Spark性能优化:数据倾斜调优

    前言 继 Spark性能优化 开发调优篇 和 Spark性能优化 资源调优篇 讲解了每个Spark开发人员都必须熟知的开发调优与资源调优之后 本文作为 Spark性能优化指南 的高级篇 将深入分析数据倾斜调优与shuffle调优 以解决更加
  • 设计补偿器网络以改善开关频率响应

    直流开关电压转换器 或 开关调节器 控制回路的特点是频率响应 频率响应影响开关调节器的反应时间对瞬态变化 精度和稳定性的影响 并在输入电压 负载和工作周期变化的情况下 如何保持设定的电压输出 工程师可以通过增加补偿器网络来改善开关调节器的频
  • Linux在云服务器上安装JDK

    官网地址下载 Java Downloads Oracle 将下载好的jdk通过Xftp传输到服务器上去 创建一个文件夹用于区分 在home文件夹下创建一个属于自己的文件夹 将需要的文件传输过去 也可以直接在 usr local 下配置 cd
  • win7电脑最新版微信卡死问题的解决

    最近一段时间无论单位还是家里事情都比较多 导致没有时间学习和写文 排名蹭蹭地往下掉 刚好遇到一个win7版微信卡死的问题 在网上查了下 找到了win10相关的可以参考的解决办法 确实有效 在此介绍一下 一 问题现象 当最小化win7托盘的微
  • linux环境安装php fileinfo扩展

    linux环境安装php fileinfo扩展 windows环境安装扩展比较简单 只需要把dll拷贝到扩展目录 修改php ini中相应的扩展就好了 下面来介绍一下linux环境下的php扩展安装 以centos6 5和php7 1为例
  • C++ OpenCV制作九宫格拼图游戏

    学更好的别人 做更好的自己 微卡智享 本文长度为2498字 预计阅读7分钟 前言 上一篇 C OpenCV生成九宫格图像 介绍了如何将图片分割城九宫格 然后重新打乱了顺序显示出来 本篇就来说一下怎么制作一个九宫格的拼图游戏 项目的重新创建了
  • JAVA是什么意思

    JAVA的意思是计算机的编程语言 Java通过面向对象的编程语言 它不仅吸收了C 语言的优点 而且摒弃了C 中难于理解的多继承和指针的概念 具有简单性 功能强大 分布式 健壮性 安全性 平台独立与可移植性 多线程及动态性的特点 Java语言
  • 第二十一课,几何着色器(基础篇)

    几何着色器的作用 输入 输入类型 从顶点着色器接收下列任何一个图元值 类型 数组大小 points 绘制GL POINTS图元时 1 lines 绘制GL LINES或GL LINE STRIP时 2 lines adjacency GL
  • seaborn学习笔记(二):散点图、线图

    html font family sans serif ms text size adjust 100 webkit text size adjust 100 body margin 0 article aside details figc
  • Spring Cloud Gateway替代zuul作为API网关(一)

    本文简要介绍如何使用Spring Cloud Gateway 作为API 网关 不是使用zuul作为网关 关于Spring Cloud Gateway和zuul的性能比较本文不再赘述 基本可以肯定Spring Cloud Finchley版
  • matlab 中.*和*有什么区别

    和 的区别 在进行数之间的运算时 和 是没有区别的 都是表示普通的乘法运算 例 m 2 n 3 m n 6 m n 6 在进行矩阵之间的运算时 和 的意义就有所不同了 假设a b表示两个矩阵 a b表示矩阵a与矩阵b进行矩阵相乘 a b表示
  • 微软服务器dda,实战DDA硬件直通:Hyper-V虚拟机直通NVMe固态硬盘

    虚拟机可以把一台电脑模拟成许多台完整的系统 并在他们当中安装运行各自的操作系统和软件应用 在使用虚拟机的时候我们既可以利用NVMe固态硬盘的性能 同时承载多个虚拟机的读写请求 也可以让其中某个需要重负载读写的虚拟机独享它的性能 这时就需要用
  • VsCode搭建Windows C++ (MSVC)开发环境

    由于最近的学习需求 折腾起了vscode 毕竟是跨平台 对以后项目的拓展也很方便 至于为什么不用mingw tdm gcc一类 主要因为毕竟是Windows平台 使用自家的MSVC开发环境一来可以放心 少出BUG 二来能够增强Windows
  • kmeans算法原理以及实践操作

    原文 http www cnblogs com dudumiaomiao p 5839905 html kmeans算法原理以及实践操作 多种k值确定以及如何选取初始点方法 kmeans一般在数据分析前期使用 选取适当的k 将数据聚类后 然
  • springboot搭建文件预览解决方案,支持目前主流格式office文件,txt文件,png,jpg等图片以及压缩文件的在线预览功能

    应用场景及实现思路 应用场景 给定一个网址 输入网址后立即显示预览文件 实现思路 1 将文件下载到本地 存储到某个指定目录 2 进行文件转换 此处是重点 3 进行文件展示 实现过程 首先 搭建一个springboot项目 搭建完毕后项目结构