【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0

【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0

  • pom
  • 上传路径工具类
  • SpringMvc虚拟路径映射
  • doc转html工具类
  • domain
  • service
  • controller
  • 前端html
  • 效果

由于上一遍文章
springboot实现上传doc&docx文件格式转pdf在线预览使用documents4j依赖只能在windows服务器(远程API)或者安装了offices实现效果。部署到linux服务器中就会转换失败。这篇文章介绍如何解决这个问题。我们可以把doc & docx文件格式转成html文件,然后以流的方式输出到浏览器就可以实现在线预览doc和docx文件的效果。话不多说,上才艺

pom

doc&docx转html的依赖


<dependency>
    <groupId>org.apache.poigroupId>
    <artifactId>poiartifactId>
    <version>3.15version>
dependency>
<dependency>
    <groupId>org.apache.poigroupId>
    <artifactId>poi-scratchpadartifactId>
    <version>3.15version>
dependency>
<dependency>
    <groupId>org.apache.poigroupId>
    <artifactId>poi-ooxmlartifactId>
    <version>3.15version>
dependency>
<dependency>
    <groupId>fr.opensagres.xdocreportgroupId>
    <artifactId>xdocreportartifactId>
    <version>1.0.6version>
dependency>
<dependency>
    <groupId>org.apache.poigroupId>
    <artifactId>poi-ooxml-schemasartifactId>
    <version>3.15version>
dependency>
<dependency>
    <groupId>org.apache.poigroupId>
    <artifactId>ooxml-schemasartifactId>
    <version>1.3version>
dependency>

上传路径工具类

根据不同的系统环境区分不同的上传目录,Windows环境为\,而Linux环境为/,这里主要根据程序当前路径创建了upload/document目录存放文档和upload/images目录存放图片

  • 使用System.getProperty(“os.name”);获取当前系统名
  • 使用System.getProperty(“user.dir”);获得程序当前路径
@Slf4j
public class UploadPathUtil {
    public static ArrayList<String> getSystemRoot() {

        /**
         * 存放doc,docx,pdf 文件上传路径
         * 存放images 文件上传路径
         */
        ArrayList<String> list = new ArrayList<>(2);


        String osName = System.getProperty("os.name");

        // windows目录
        String windowsUpload = System.getProperty("user.dir")+"\\upload\\";

        // linux目录
        String linuxUpload = System.getProperty("user.dir")+"/upload/";
        log.info("当前系统为: "+osName);

        if (osName.toLowerCase().startsWith("linux")) {
            list.add(linuxUpload+"document/");
            list.add(linuxUpload+"images/");
            return list;
        } else if (osName.toLowerCase().startsWith("windows")) {
            list.add(windowsUpload+"document\\");
            list.add(windowsUpload+"images\\");
            return list;
        } else {
            throw new RuntimeException("错误目录");
        }
    }

    public static void main(String[] args) {
        ArrayList<String> systemRoot = UploadPathUtil.getSystemRoot();
        System.out.println(systemRoot);

    }
}

SpringMvc虚拟路径映射

application.yml中添加静态资源路径
在这里插入图片描述
在spring mvc配置类中使用@Value获取配置文件的值,并把静态资源路径和虚拟路径映射关联。

前端访问/api/static/document/****表示任意目录文件。虚拟映射到程序当前路径的upload目录的document目录下。

前端访问/api/static/images/****表示任意目录文件。虚拟映射到程序当前路径的upload目录的images目录下。

@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {

    // 文档上传路径
    @Value("${file.staticAccessDocumentPath}")
    private String staticAccessDocumentPath;

    // 图片上传路径
    @Value("${file.staticAccessImagesPath}")
    private String staticAccessImagesPath;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        // 登录请求访问登录页面
        registry.addViewController("/toLogin").setViewName("login.html");
    }

    /**
     * 虚拟路径映射
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        ArrayList<String> systemRoot = UploadPathUtil.getSystemRoot();

        log.info("systemRoot: "+systemRoot);
        // 在yml配置文件中读取路径
        registry.addResourceHandler(staticAccessDocumentPath).addResourceLocations("file:"+systemRoot.get(0));

        registry.addResourceHandler(staticAccessImagesPath).addResourceLocations("file:"+systemRoot.get(1));
    }
}

doc转html工具类

public class Doc2Html {

    // 上传路径
    public static final ArrayList<String> uploadPath = UploadPathUtil.getSystemRoot();

    /**
     * doc转换为html
     * @param fileName docx文件路径(如果你的是存到文件服务器的,直接用路径,如果是直接将文件流存到数据库的,转为InputStream )
     * @param outPutFile  html输出文件路径
     * @throws TransformerException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public static void doc2Html(String fileName, String outPutFile)
            throws TransformerException, IOException, ParserConfigurationException {
        long startTime = System.currentTimeMillis();
        // HWPFDocument wordDocument = new HWPFDocument(fileName);
        HWPFDocument wordDocument = new HWPFDocument(new FileInputStream(fileName));
        WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
                DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
        wordToHtmlConverter.setPicturesManager(new PicturesManager() {
            public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches,
                                      float heightInches) {
                // 设置虚拟访问路径
                return "/api/static/images/" + suggestedName;
            }
        });
        wordToHtmlConverter.processDocument(wordDocument);
        // 保存图片
        List<Picture> pics = wordDocument.getPicturesTable().getAllPictures();

        if (pics != null) {
            for (int i = 0; i < pics.size(); i++) {
                Picture pic = (Picture) pics.get(i);

                String suggestFullFileName = pic.suggestFullFileName();
                System.out.println(suggestFullFileName);
                try {
                    File windowsImageUpload = new File(uploadPath.get(1));
                    if (!windowsImageUpload.exists()){
                        windowsImageUpload.mkdirs();
                    }

                    pic.writeImageContent(new FileOutputStream(windowsImageUpload +"\\"+ pic.suggestFullFileName()));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }

        Document htmlDocument = wordToHtmlConverter.getDocument();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        DOMSource domSource = new DOMSource(htmlDocument);
        StreamResult streamResult = new StreamResult(out);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer serializer = tf.newTransformer();
        serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
        serializer.setOutputProperty(OutputKeys.INDENT, "yes");
        serializer.setOutputProperty(OutputKeys.METHOD, "html");
        serializer.transform(domSource, streamResult);
        out.close();
        writeFile(new String(out.toByteArray()), outPutFile);
        System.out.println("Generate " + outPutFile + " with " + (System.currentTimeMillis() - startTime) + " ms.");
    }

    /**
     * 写文件
     *
     * @param content
     * @param path
     */
    public static void writeFile(String content, String path) {
        FileOutputStream fos = null;
        BufferedWriter bw = null;
        try {
            File file = new File(path);
            fos = new FileOutputStream(file);
            bw = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"));
            bw.write(content);
        } catch (FileNotFoundException fnfe) {
            fnfe.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            try {
                if (bw != null)
                    bw.close();
                if (fos != null)
                    fos.close();
            } catch (IOException ie) {
            }
        }
    }

    /**
     * docx格式word转换为html
     *
     * @param fileName
     *            docx文件路径(如果你的是存到文件服务器的,直接用路径,如果是直接将文件流存到数据库的,转为InputStream )
     * @param outPutFile
     *            html输出文件路径
     * @throws TransformerException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public static void docx2Html(String fileName, String outPutFile)
            throws TransformerException, IOException, ParserConfigurationException {
        String fileOutName = outPutFile;
        long startTime = System.currentTimeMillis();
        XWPFDocument document = new XWPFDocument(new FileInputStream(fileName));
        //XWPFDocument document = new XWPFDocument(fileName);
        XHTMLOptions options = XHTMLOptions.create().indent(4);
        // 导出图片
        File imageFolder = new File(uploadPath.get(1));
        options.setExtractor(new FileImageExtractor(imageFolder));
        // URI resolver
        //options.URIResolver(new FileURIResolver(imageFolder));

       // 设置虚拟访问路径 docx导出图片会另外创建/word/media目录不影响虚拟访问
        options.URIResolver(new BasicURIResolver("/api/static/images/"));
        File outFile = new File(fileOutName);

        outFile.getParentFile().mkdirs();

        OutputStream out = new FileOutputStream(outFile);
        XHTMLConverter.getInstance().convert(document, out, options);
        System.out.println("Generate " + fileOutName + " with " + (System.currentTimeMillis() - startTime) + " ms.");

    }

    public static void main(String[] args) throws Exception {
        Doc2Html.docx2Html("F:\\2017-2020级学生2021-2022学年缴费通知.docx","F:\\cs.html");
    }

}

domain

@Data
@TableName("tb_interface_doc")
public class InterfaceDoc extends SuperEntity{

    @TableField("url_address")
    private String urlAddress;

    @TableField("doc_name")
    private String docName;

    @TableField("suffix")
    private String suffix;

    @TableField("project_id")
    private Integer projectId;
}

service

我这里是直接把构建生成的randomNumber + extensionrandomNumber.html保存到了数据库中,然后在前端以超链接打开一个窗口的方式显示,以实现pdf和doc&docx在线阅读的效果。当然还可以以流的方式输出到浏览器直接显示。根据自己需求选择

/**
     * 上传接口文档
     * @param multipartFile
     * @param req
     * @param id
     * @return
     */
    @Override
    public Boolean uploadInterfaceWord(MultipartFile multipartFile, HttpServletRequest req, Integer id) throws Exception {
        //使用UUID生成唯一标识文件名
        String randomNumber = UUID.randomUUID().toString().replace("-", "");
        //获取文件的原始名
        String oldFilename = multipartFile.getOriginalFilename();
        //获取文件后缀 .pdf/.docx/.doc
        String extension = oldFilename.substring(oldFilename.lastIndexOf("."));
        //生成新的文件名
        String newFileName = randomNumber + extension;


        // 文档上传路径
        File documentUpload = new File(systemRoot.get(0));
        if (!documentUpload.exists()) {
            //判断目录是否存在,不存在则直接创建
            documentUpload.mkdirs();
        }
        try {
            multipartFile.transferTo(new File(documentUpload, newFileName));
        } catch (IOException e) {
            e.printStackTrace();
        }


        // InputStream input = null;
        // OutputStream out = null;

        // 虚拟路径
        String invented_address = null;

        // 文件后缀,方便前端判断
        String fileSuffix = null;

        // 构建doc和docx新的文件名
        String docAndDocxNewFileName =  randomNumber + ".html";

        if (extension.equals(".pdf")){
            // 上传文件为.pdf后缀的虚拟访问路径
            invented_address  = req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort() + "/api/static/document/" + newFileName;
            // 添加后缀
            fileSuffix = extension.substring(extension.indexOf(".") + 1);
        } else

            // 在线预览
            //获取文件路径
            if (extension.equals(".docx")) {

                Doc2Html.docx2Html(systemRoot.get(0) + newFileName, systemRoot.get(0) + docAndDocxNewFileName);

                // 上传文件为.pdf后缀的虚拟访问路径
                invented_address  = req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort() + "/api/static/document/" + docAndDocxNewFileName;
                // 添加后缀
                fileSuffix = extension.substring(extension.indexOf(".") + 1);

//                input = new FileInputStream(systemRoot.get(0) + docAndDocxNewFileName);
//                response.setContentType("text/html;charset=UTF-8");// 解决页面显示乱码
//                out = response.getOutputStream();
            } else if (extension.equals(".doc")) {
                Doc2Html.doc2Html(systemRoot.get(0) + newFileName, systemRoot.get(0) + docAndDocxNewFileName);

                // 上传文件为.pdf后缀的虚拟访问路径
                invented_address  = req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort() + "/api/static/document/" + docAndDocxNewFileName;
                // 添加后缀
                fileSuffix = extension.substring(extension.indexOf(".") + 1);

//                input = new FileInputStream(systemRoot.get(0) + docAndDocxNewFileName);
//                response.setContentType("text/html;charset=UTF-8");// 解决页面显示乱码
//                out = response.getOutputStream();
            }

        InterfaceDoc interfaceDoc = new InterfaceDoc();
        interfaceDoc.setProjectId(id);
        interfaceDoc.setUrlAddress(invented_address);
        interfaceDoc.setSuffix(fileSuffix);
        interfaceDoc.setDocName(oldFilename);
        interfaceDocMapper.insert(interfaceDoc);

        return true;
		// 以流的方式输出到浏览器
//        if (extension.equals(".docx") || extension.equals(".doc")) {
//            FileInputStream fis = new FileInputStream(systemRoot.get(0) + docAndDocxNewFileName);
//            byte[] b = new byte[fis.available()];
//            if (out != null) {
//                if (input != null) {
//                    input.read(b);
//                    out.write(b);
//                } else {
//                    System.out.println("InputStream为空。。。");
//                }
//            } else {
//                System.out.println("OutputStream为空。。。");
//            }
//
//            out.flush();
//            input.close();
//            out.close();
//        }

    }

【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0_第1张图片

controller

@PostMapping("/project/upload/{id}")
public ResultVO<?> fileupload(MultipartFile file, HttpServletRequest req,@PathVariable Integer id) throws FileNotFoundException {

    Boolean bool = projectService.uploadInterfaceWord(file, req, id);

    return bool ?  ResultVO.ok("上传成功") : ResultVO.fail("上传失败");
}

前端html

DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>查看接口文档title>
    <link rel="stylesheet" th:href="@{/component/pear/css/pear.css}" />
head>
<body class="pear-container">

    <div>
        <div class="layui-row layui-col-space10">
            <div class="layui-col-xs6 layui-col-md3" th:each="item:${InterfaceDocList}">
                <div class="layui-card top-panel">
                    <div class="layui-card-header">[[${item.docName}]]div>
                    <div class="layui-card-body">
                        <div class="layui-row layui-col-space5">
                            <div class="layui-col-xs8 layui-col-md8 top-panel-number" style="color: #28333E;">
                                <a th:href="@{${item.urlAddress}}" target="_blank">
                                    <img th:src="@{'/admin/images/'+${item.suffix}+'.png'}" alt="查看详情"  style="width: 100px;height: 100px;border-radius: 5px;" />
                                a>
                            div>
                        div>
                    div>
                div>
            div>
        div>
    div>
body>
<script th:src="@{/component/layui/layui.js}">script>
<script th:src="@{/component/pear/pear.js}">script>
html>

效果

1、上传接口文件
【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0_第2张图片
2、上传相关接口文档
【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0_第3张图片
3、查看所有接口文档

【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0_第4张图片
4、查看接口详情
【SpringBoot高级篇】springboot实现上传doc&docx文件格式转html在线预览v2.0_第5张图片

你可能感兴趣的:(#,SpringBoot,springboot,文件上传)