• 使用Spring Gateway为对象存储系统MinIo和kkFileView文档预览增加登录验证


    官网介绍:kkFileView为文件文档在线预览解决方案,该项目使用流行的spring boot搭建,易上手和部署,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等。

    前一文章《开源对象存储系统MinIo部署配置与SpringBoot客户端整合访问》已经介绍MinIo部署和SpringBoot的基本整合。
    本文介绍kkFileView的部署,并可以预览MiniIo存储的文档。
    kkFileView没有权限认证功能,本文使用Spring Gateway为kkFileView增加登录才能预览文档的功能。

    1、kkfileview下载部署

    官网:https://kkview.cn
    码云源码:https://gitee.com/kekingcn/file-online-preview

    1.1、安装包部署运行

    1.1.1、物理机或虚拟机上运行

    自v4.1.0 版本开始 kkFileView 不再免费提供 Windows 和 Linux 的发行安装包了,可以自己基于源码编译打包运行。
    本文测试用的4.0.0版本来自文章:https://blog.csdn.net/qq_37284798/article/details/129377354提供。
    解压kkFileView-x.x.x文件(Windows用.zip包,Linux/MacOS用.tar.gz包)
    打开解压后文件夹的bin目录,运行startup脚本(Windows下以管理员身份运行startup.bat,Linux以root用户运行startup.sh)
    浏览器访问本机8012端口 http://127.0.0.1:8012 即可看到项目演示用首页

    1.1.2、Docker容器环境环境运行

    使用docker部署

    # 拉取镜像 
    # docker中央仓库https://hub.docker.com/r/keking/kkfileview,仓库最新版本只有4.1.0版本;Gitee源码最新版本为4.3.0
    docker pull keking/kkfileview:4.1.0
    
    # 运行
    docker run -it -p 8012:8012 keking/kkfileview:4.1.0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    浏览器访问容器8012端口 http://127.0.0.1:8012 即可看到项目演示用首页

    使用docker-compose部署

    # 配置文件docker-compose.yml
    version: '3'
    services:
      web:
        restart: always
        container_name: kkfileview
        image: keking/kkfileview:4.1.0
        ports:
          - "8012:8012"
        privileged: true
        volumes:
          - "./config/application.properties:/opt/kkFileView-4.1.0/config/application.properties"
          - "./log:/opt/kkFileView-4.1.0/log"
          
    ## 启动命令
    docker-compose up -d
    ## 关闭
    docker-compose down
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    kkfileview官方参考配置application.properties内容

    #######################################不可动态配置,需要重启生效#######################################
    server.port = ${KK_SERVER_PORT:8012}
    #server.servlet.context-path= ${KK_CONTEXT_PATH:/}
    server.servlet.context-path= /preview
    server.servlet.encoding.charset = utf-8
    #文件上传限制
    spring.servlet.multipart.max-file-size=500MB
    spring.servlet.multipart.max-request-size=500MB
    ## Freemarker 配置
    spring.freemarker.template-loader-path = classpath:/web/
    spring.freemarker.cache = false
    spring.freemarker.charset = UTF-8
    spring.freemarker.check-template-location = true
    spring.freemarker.content-type = text/html
    spring.freemarker.expose-request-attributes = true
    spring.freemarker.expose-session-attributes = true
    spring.freemarker.request-context-attribute = request
    spring.freemarker.suffix = .ftl
    
    # office-plugin
    ## office转换服务的进程数,默认开启两个进程
    office.plugin.server.ports = 2001,2002
    ## office 转换服务 task 超时时间,默认五分钟
    office.plugin.task.timeout = 5m
    
    #预览生成资源路径(默认为打包根路径下的file目录下)
    #file.dir = D:\\kkFileview\\
    file.dir = ${KK_FILE_DIR:default}
    
    #允许预览的本地文件夹 默认不允许任何本地文件被预览
    #file.dir = D:\\kkFileview\\
    local.preview.dir = ${KK_LOCAL_PREVIEW_DIR:default}
    
    
    #openoffice home路径
    #office.home = C:\\Program Files (x86)\\OpenOffice 4
    office.home = ${KK_OFFICE_HOME:default}
    
    #缓存实现类型,不配默认为内嵌RocksDB(type = default)实现,可配置为redis(type = redis)实现(需要配置spring.redisson.address等参数)和 JDK 内置对象实现(type = jdk),
    cache.type =  ${KK_CACHE_TYPE:jdk}
    #redis连接,只有当cache.type = redis时才有用
    spring.redisson.address = ${KK_SPRING_REDISSON_ADDRESS:127.0.0.1:6379}
    spring.redisson.password = ${KK_SPRING_REDISSON_PASSWORD:}
    #缓存是否自动清理 true 为开启,注释掉或其他值都为关闭
    cache.clean.enabled = ${KK_CACHE_CLEAN_ENABLED:true}
    #缓存自动清理时间,cache.clean.enabled = true时才有用,cron表达式,基于Quartz cron
    cache.clean.cron = ${KK_CACHE_CLEAN_CRON:0 0 3 * * ?}
    
    #######################################可在运行时动态配置#######################################
    #提供预览服务的地址,默认从请求url读,如果使用nginx等反向代理,需要手动设置
    #base.url = https://file.keking.cn
    #base.url = ${KK_BASE_URL:default}
    base.url = http://127.0.0.1:8012/preview
    
    #信任站点,多个用','隔开,设置了之后,会限制只能预览来自信任站点列表的文件,默认不限制
    #trust.host = file.keking.cn,kkfileview.keking.cn
    trust.host = ${KK_TRUST_HOST:default}
    
    #是否启用缓存
    cache.enabled = ${KK_CACHE_ENABLED:true}
    
    #文本类型,默认如下,可自定义添加
    simText = ${KK_SIMTEXT:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}
    #多媒体类型,默认如下,可自定义添加
    media = ${KK_MEDIA:mp3,wav,mp4,flv}
    #是否开启多媒体类型转视频格式转换,目前可转换视频格式有:avi,mov,wmv,3gp,rm
    #请谨慎开启此功能,建议异步调用添加到处理队列,并且增加任务队列处理线程,防止视频转换占用完线程资源,转换比较耗费时间,并且控制了只能串行处理转换任务
    media.convert.disable = ${KK_MEDIA_CONVERT_DISABLE:false}
    #支持转换的视频类型
    convertMedias = ${KK_CONVERTMEDIAS:avi,mov,wmv,mkv,3gp,rm}
    #office类型文档(word ppt)样式,默认为图片(image),可配置为pdf(预览时也有按钮切换)
    office.preview.type = ${KK_OFFICE_PREVIEW_TYPE:image}
    #是否关闭office预览切换开关,默认为false,可配置为true关闭
    office.preview.switch.disabled = ${KK_OFFICE_PREVIEW_SWITCH_DISABLED:false}
    
    #是否禁止演示模式
    pdf.presentationMode.disable = ${KK_PDF_PRESENTATION_MODE_DISABLE:true}
    #是否禁止打开文件
    pdf.openFile.disable = ${KK_PDF_OPEN_FILE_DISABLE:true}
    #是否禁止打印转换生成的pdf文件
    pdf.print.disable = ${KK_PDF_PRINT_DISABLE:true}
    #是否禁止下载转换生成的pdf文件
    pdf.download.disable = ${KK_PDF_DOWNLOAD_DISABLE:true}
    #是否禁止bookmark
    pdf.bookmark.disable = ${KK_PDF_BOOKMARK_DISABLE:true}
    #是否禁用首页文件上传
    file.upload.disable = ${KK_FILE_UPLOAD_ENABLED:false}
    
    #预览源为FTP时 FTP用户名,可在ftp url后面加参数ftp.username=ftpuser指定,不指定默认用配置的
    ftp.username = ${KK_FTP_USERNAME:ftpuser}
    #预览源为FTP时 FTP密码,可在ftp url后面加参数ftp.password=123456指定,不指定默认用配置的
    ftp.password = ${KK_FTP_PASSWORD:123456}
    #预览源为FTP时, FTP连接默认ControlEncoding(根据FTP服务器操作系统选择,Linux一般为UTF-8,Windows一般为GBK),可在ftp url后面加参数ftp.control.encoding=UTF-8指定,不指定默认用配置的
    ftp.control.encoding = ${KK_FTP_CONTROL_ENCODING:UTF-8}
    
    #水印内容
    #例:watermark.txt = ${WATERMARK_TXT:凯京科技内部文件,严禁外泄}
    #如需取消水印,内容设置为空即可,例:watermark.txt = ${WATERMARK_TXT:}
    watermark.txt = ${WATERMARK_TXT:}
    #水印x轴间隔
    watermark.x.space = ${WATERMARK_X_SPACE:10}
    #水印y轴间隔
    watermark.y.space = ${WATERMARK_Y_SPACE:10}
    #水印字体
    watermark.font = ${WATERMARK_FONT:微软雅黑}
    #水印字体大小
    watermark.fontsize = ${WATERMARK_FONTSIZE:18px}
    #水印字体颜色
    watermark.color = ${WATERMARK_COLOR:black}
    #水印透明度,要求设置在大于等于0.005,小于1
    watermark.alpha = ${WATERMARK_ALPHA:0.2}
    #水印宽度
    watermark.width = ${WATERMARK_WIDTH:180}
    #水印高度
    watermark.height = ${WATERMARK_HEIGHT:80}
    #水印倾斜度数,要求设置在大于等于0,小于90
    watermark.angle = ${WATERMARK_ANGLE:10}
    
    #Tif类型图片浏览模式:tif(利用前端js插件浏览);jpg(转换为jpg后前端显示);pdf(转换为pdf后显示,便于打印)
    tif.preview.type = ${KK_TIF_PREVIEW_TYPE:tif}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    1.2、接入说明

    参考自官方Demo接入说明:http://127.0.0.1:8012/index
    如果你的项目需要接入文件预览项目,达到对docx、excel、ppt、jpg等文件的预览效果,那么通过在你的项目中加入下面的代码就可以成功实现:

    var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
    window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(base64Encode(url)));
    
    • 1
    • 2

    新增多图片同时预览功能,接口如下:

    var fileUrl =url1+'|'+url2;//多url使用'|'字符隔开
    window.open('http://127.0.0.1:8012/picturesPreview?urls='+encodeURIComponent(base64Encode(fileUrl)));
    
    • 1
    • 2

    2、使用Spring Gateway增加登录认证

    2.1、网关实现代码

    Spring Gateway使用前文的例子:Spring Cloud 2022.x版本使用gateway和nacos实现动态路由和负载均衡
    增加一个全局过滤器类FileViewFilter.java,检查用户登录cookie是否存在。

    package com.penngo.gateway.filter;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpCookie;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    import java.util.List;
    
    @Component
    public class FileViewFilter implements GlobalFilter, Ordered {
        @Override
        public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            try{
                ServerHttpRequest request = exchange.getRequest();
                HttpHeaders headers = request.getHeaders();
                ServerHttpResponse response = exchange.getResponse();
                MultiValueMap cookieMap = request.getCookies();
                String currentUrl = request.getURI().getPath();
                if(currentUrl.startsWith("/preview")){
                    boolean isForbid = true;
                    if(cookieMap != null ){
                        List cookies = cookieMap.get("token");
                        if(cookies != null){
                            for(HttpCookie cookie:cookies){
                                if(cookie.getName().equals("token") && cookie.getValue().equals("test123456")){
                                    isForbid = false;
                                }
                            }
                        }
                    }
                    if(isForbid == false){
                        return chain.filter(exchange);
                    }
                    else{
                        DataBuffer dataBuffer = response.bufferFactory().wrap("No permission, please login first!".getBytes());
                        response.setStatusCode(HttpStatus.OK);
                        return response.writeWith(Flux.just(dataBuffer));
                    }
                }
            }
            catch(Exception e){
                e.printStackTrace();
            }
            return chain.filter(exchange);
        }
        @Override
        public int getOrder() {
            return -200;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    Nacos配置:

    spring:
      cloud:
        gateway:
          routes:
            - id: previewid
              uri: http://192.168.245.139:8012/
              predicates:
                - Path=/preview/**
            - id: fileid
              uri: http://127.0.0.1:8080/
              predicates:
                - Path=/index/**
              filters:
                - StripPrefix=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2、文件服务实现代码

    文档预览服务基于开源对象存储系统minio部署配置与SpringBoot客户端整合访问上修改。
    pom.xml增加依赖包:

    
        org.springframework.boot
        spring-boot-starter-thymeleaf
    
    
    • 1
    • 2
    • 3
    • 4

    application.yml增加thymeleaf配置:

    server:
      port: 8080
      servlet:
        encoding:
          force: true
          charset: UTF-8
          enabled: true
      tomcat:
        uri-encoding: UTF-8
      thymeleaf:
        mode: HTML
        cache: false
        encoding: UTF-8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    FileController增加方法:

    package com.penngo.example.controlleer;
    
    import com.penngo.example.component.MinioUtils;
    import jakarta.annotation.Resource;
    import jakarta.servlet.http.Cookie;
    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.*;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.net.URLEncoder;
    import java.util.Base64;
    import java.util.HashMap;
    import java.util.Map;
    
    @Controller
    public class FileController {
    
        @Resource
        private MinioUtils minioUtils;
    
    
        @GetMapping("/fileListView")
        public String fileListView(HttpServletRequest request, Model model){
            Map<String, String> files =  minioUtils.getFileList();
            StringBuffer sff = new StringBuffer();
            Base64.Encoder encoder = Base64.getEncoder();
            Map<String, String> fileUrls =  new HashMap<>();
            files.forEach((k,v)->{
                String url = encoder.encodeToString(v.getBytes());
                url = "http://127.0.0.1:9090/preview/onlinePreview?url=" + URLEncoder.encode(url);
                fileUrls.put(k, url);
            });
            model.addAttribute("files",fileUrls);
            return "/index/fileList";
        }
        @RequestMapping("/login")
        public String login(HttpServletRequest request) {
            return "/index/login";
        }
    
        @RequestMapping( value ="/save", method = RequestMethod.POST)
        public String save(HttpServletRequest request, HttpServletResponse response, Model model, @RequestParam String user, @RequestParam String passwd){
            model.addAttribute("stat", "登录失败");
            if(user.equals("admin") && passwd.equals("123456")){
                model.addAttribute("stat", "登录成功");
                Cookie[] Cookies =  request.getCookies();
                Cookie cookie = new Cookie("token", "test123456");
                cookie.setPath("/");
                cookie.setMaxAge(60 * 60 * 24);
                response.addCookie(cookie);
            }
            return "/index/login";
        }
    
        @RequestMapping( value ="/logout", method = RequestMethod.GET)
        public String logout(HttpServletRequest request, HttpServletResponse response, Model model){
            Map<String, String> data = new HashMap<>();
            model.addAttribute("stat", "退出成功");
            Cookie cookie = new Cookie("token", "");
            cookie.setPath("/");
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            return "/index/logout";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    resources\templates\index\login.html

    
    
    
        
        Title
    
    
    
    
    用户名:
    密 码:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    模板文件
    fileList.html

    
    
    
        
        Title
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    login.html

    
    
    
        
        Title
    
    
    
    
    用户名:
    密 码:
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.3、Demo运行效果

    文件浏览列表,例子只放了一个office文档
    访问文件列表
    在这里插入图片描述
    未登录前访问文档,提示“没有权限,请先登录”
    在这里插入图片描述
    登录
    在这里插入图片描述

    在这里插入图片描述
    登录后再访问文档地址,已能成功预览docx文档

  • 相关阅读:
    正规现货黄金中的MACD技术
    PY32F003F18之RTC
    2023-10-12 LeetCode每日一题(找出数组的串联值)
    xilinx zynq7系列加载器无法连接的原因&测试xilinx Zynq7开发板的加载器和芯片是否正常的快速方法
    开发这么久,gradle 和 gradlew 啥区别、怎么选?
    关于GBDT算法、XGBoost算法的基本原理概述
    Ros Launch启动管理
    防抖和节流是什么?防抖和节流的使用场景(附源代码分享)
    TSN时间敏感网络
    79. 单词搜索
  • 原文地址:https://blog.csdn.net/penngo/article/details/132763329