Snapan项目--预览文件梳理

整体思路

前端触发预览请求--->后端生成安全预览链接--->前端按文件类型适配渲染--->关闭预览

触发预览、前端请求预览链接

功能说明

用户点击文件预览按钮后,前端展示预览模态框并初始化加载状态。

核心代码【home.html】

动态生成文件列表时:

java 复制代码
 <div class="action-item" onclick="previewFile('${file.filePath}')" data-tooltip="预览"><button class="action-icon preview">👁️</button></div>
html 复制代码
    <!-- 预览模态框 -->
    <div id="previewModal" class="modal" style="display: none;">
        <div class="modal-content" style="width: 90%; max-width: 1200px; height: 90vh; max-height: 900px; padding: 0;">
            <div class="modal-header">
                <h3 id="previewModalTitle">文件预览</h3>
                <span class="close" onclick="closePreviewModal()">&times;</span>
            </div>
            <!-- 预览容器(放在模态框内) -->
            <div id="previewContainer" style="width: 100%; height: calc(100% - 60px); border: none; border-radius: 0;">
                <div class="loading">
                    <span>正在生成预览链接,请稍候...</span>
                </div>
            </div>
        </div>
    </div>

前端调用/api/filebase/getPreviewUrl接口,传递文件路径参数,处理成功/失败响应;

java 复制代码
        // 1. 预览函数(点击预览按钮时调用)
        function previewFile(filePath) {
            // 显示预览模态框
            document.getElementById("previewModal").style.display = "flex";
            // 更新模态框标题(显示当前预览文件名)
            document.getElementById("previewModalTitle").textContent = `文件预览:${filePath.split("/").pop()}`;
            // 调用后端接口获取签名预览URL(使用你的项目基础路径)
            const baseUrl = "http://localhost:8080"; // 与你的后端地址一致
            $.ajax({
                url: `${baseUrl}/api/filebase/getPreviewUrl`,
                type: "GET",
                data: { filePath: filePath }, // 传递当前文件的OSS路径
                dataType: "json",
                success: function (res) {
                    if (res.code === "200") {
                        const previewUrl = res.previewUrl;
                        const fileType = res.fileType;
                        // 渲染预览内容
                        renderPreview(previewUrl, fileType);
                        console.log(filePath,fileType,previewUrl);
                    } else {
                        const container = document.getElementById("previewContainer");
                        container.innerHTML = `
                    <div class="error">
                        <span>${res.msg || "生成预览链接失败,请重试"}</span>
                    </div>
                `;
                    }
                },
                error: function (xhr, status, error) {
                    const container = document.getElementById("previewContainer");
                    container.innerHTML = `
                <div class="error">
                    <span>预览接口访问失败:${error}</span>
                    <br>
                    <span style="margin-top:5px;display:inline-block;">请检查接口地址是否正确,或是否存在跨域问题</span>
                </div>
            `;
                }
            });
        }

后端生成预览链接、前端渲染预览内容

功能说明

后端接收文件路径,处理oss域名,生成带有有效期的签名预览url,并提取文件类型。

核心代码

java 复制代码
 /**
     * 获取文件预览的签名url
     * @param filePath oss文件中的路径
     * @return 包含url和文件类型的json
     */
    @GetMapping("/getPreviewUrl")
    @ResponseBody
    public Map<String,String> getPreviewUrl(String filePath){
        Map<String,String> result=new HashMap<>();
        try{
            //生成1小时有效的签名url
            // 手动去除URL中的域名,只保留相对路径
            String ossDomain = "https://snapan.oss-cn-beijing.aliyuncs.com/";
            if (filePath.startsWith(ossDomain)) {
                filePath = filePath.substring(ossDomain.length()); // 截取后得到 "1761277048722.jpg"
            }
            // 生成签名URL(此时filePath是相对路径)
            String previewUrl=ossService.generatePreviewUrl(filePath,3600);
            System.out.println(previewUrl);
            System.out.println(filePath);
            //获取文件后缀
            String fileType=getFileSuffix(filePath);
            result.put("code","200");
            result.put("previewUrl",previewUrl);
            result.put("fileType",fileType);
        }catch (Exception e){
            result.put("code","500");
            result.put("msg","生成预览链接失败:"+e.getMessage());
        }
        return result;
    }

    //提取文件后缀
    private String getFileSuffix(String fileName){
        if(fileName.lastIndexOf(".")==-1){
            return "";//无后缀
        }
        return fileName.substring(fileName.lastIndexOf(".")+1).toLowerCase();
    }
java 复制代码
/**
     * 生成带签名的预览URL(用于在线预览)
     */
    public String generatePreviewUrl(String fileName, int expirationMinutes) {
        try {
            // 设置URL过期时间
            java.util.Date expiration = new java.util.Date();
            long expTimeMillis = expiration.getTime() + expirationMinutes * 60 * 1000;
            expiration.setTime(expTimeMillis);
            //创建响应头配置对象,控制浏览器是否预览
            ResponseHeaderOverrides headers=new ResponseHeaderOverrides();
            // 判断文件是否为PDF或Office文件,强制预览
            String lowerFileName = fileName.toLowerCase();
            if (lowerFileName.endsWith(".pdf") ||
                    lowerFileName.endsWith(".doc") ||
                    lowerFileName.endsWith(".docx") ||
                    lowerFileName.endsWith(".xls") ||
                    lowerFileName.endsWith(".xlsx") ||
                    lowerFileName.endsWith(".ppt") ||
                    lowerFileName.endsWith(".pptx")) {

                String fileNameOnly = fileName.substring(fileName.lastIndexOf("/") + 1);
                // 设置Content-Disposition为inline,允许浏览器预览
                headers.setContentDisposition("inline;filename=\"" + fileNameOnly + "\"");

            }
            //生成带响应头配置的签名url
            com.aliyun.oss.model.GeneratePresignedUrlRequest request=
                    new com.aliyun.oss.model.GeneratePresignedUrlRequest(bucketName,fileName);
            request.setExpiration(expiration);
            request.setMethod(HttpMethod.GET);
            request.setResponseHeaders(headers);
            // 生成带签名的URL,用于预览(GET请求)
            java.net.URL signedUrl = ossClient.generatePresignedUrl(request);
            String urlStr=signedUrl.toString();
            return urlStr;
        } catch (Exception e) {
            throw new RuntimeException("生成预览URL失败", e);
        }
    }

前端根据文件类型(图片、视频、Office 文档等),生成对应的 HTML 标签渲染预览内容。

java 复制代码
  /**
         * 4. 根据文件类型渲染不同的预览组件
         * @param {string} url - 后端返回的签名预览URL
         * @param {string} type - 后端返回的文件后缀(小写)
         */
        function renderPreview(url, type) {
            const container = document.getElementById("previewContainer");
            container.innerHTML = ""; // 清空加载状态

            switch (type) {
                // 图片类型
                case "jpg": case "jpeg": case "png": case "gif": case "webp": case "bmp":
                    container.innerHTML = `<img src="${url}" alt="图片预览" title="点击可查看原图" style="width:100%;height:100%;object-fit:contain;">`;
                    break;
                // PDF类型
                /*case "pdf":
                    container.innerHTML = `<iframe src="${url}" title="PDF预览" style="width:100%;height:100%;border:none;"></iframe>`;
                    break;*/
                // 视频类型
                case "mp4":
                case "webm":
                case "mov":
                case "avi":
                case "mkv":
                    container.innerHTML = `
                <video controls autoplay muted playsinline style="width:100%;height:100%;padding:20px;">
                    <source src="${url}" type="video/${type}">
                    您的浏览器不支持该视频格式,请下载后查看
                </video>
            `;
                    break;
                // 音频类型
                case "mp3": case "wav": case "ogg": case "flac":
                    container.innerHTML = `
                <audio controls autoplay style="width:100%;padding:20px;">
                    <source src="${url}" type="audio/${type}">
                    您的浏览器不支持该音频格式,请下载后查看
                 </audio>
            `;
                    break;
                // Office文档(依赖kkFileView)
                case "doc": case "docx": case "xls": case "xlsx": case "ppt": case "pptx":case "pdf":
                case "txt": case "md": case "json": case "xml": case "html": case "css": case "js":
                case "xmind":case "vsd":case "py":case "eml":case "psd":case "csv":
                    const kkFileViewUrl = "http://101.42.40.144:8012";
                    //const encodedUrl = encodeURIComponent(Base64.encode(url));
                    const encodedUrl = encodeURIComponent(Base64.encode(url).replace(/[\r\n]/g, '')).replace(/%0A/g, '');
                    const previewUrl = `${kkFileViewUrl}/onlinePreview?url=${encodedUrl}`;
                    console.log("office预览地址:",previewUrl);
                    container.innerHTML = `
                    <iframe
                        src="${previewUrl}"
                        title="Office文档预览"
                        style="width:100%;height:100%;border:none;"
                       onload="document.getElementById('officeLoading').style.display='none';this.style.display='block'">
                    </iframe>
                `;
                    break;
                // 其他类型
                default:
                    showDownloadTip(container, url, type);
            }
        }


        function showDownloadTip(container, url, fileType) {
            container.innerHTML = `
        <div class="download-tip" style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;">
            <span>当前文件类型(.${fileType || "未知"})不支持在线预览</span>
            <br>
        </div>
    `;
        }
  • 图片直接用img标签,通过object-fit:contain保持比例;
  • 视频 / 音频使用原生video/audio标签,支持控制、自动播放(视频默认静音);
  • Office 文档等通过iframe嵌入第三方服务(kkFileView),需对 URL 进行 Base64 编码避免格式错误;

关闭预览

java 复制代码
// 2. 新增:关闭预览模态框
        function closePreviewModal() {
            document.getElementById("previewModal").style.display = "none";
            // 清空预览容器,避免下次打开有残留
            document.getElementById("previewContainer").innerHTML = `
        <div class="loading">
            <span>正在生成预览链接,请稍候...</span>
        </div>
    `;
        }

关于kkfileview安装及使用

参考了以下博客,感谢这位大佬

https://blog.csdn.net/qq_33697094/article/details/126076565?fromshare=blogdetail&sharetype=blogdetail&sharerId=126076565&sharerefer=PC&sharesource=2301_80391652&sharefrom=from_link

相关推荐
前端不太难15 小时前
没有文档模型,HarmonyOS PC 应用会发生什么?
华为·状态模式·harmonyos
GatiArt雷17 小时前
Libvio.link网站反爬机制深度剖析与合规爬虫策略研究
状态模式
Go_Zezhou18 小时前
render快速部署网站和常见问题解决
运维·服务器·开发语言·python·github·状态模式
共享家95271 天前
搭建 AI 聊天机器人:”我的人生我做主“
前端·javascript·css·python·pycharm·html·状态模式
We1ky1 天前
从零到一:我的javascript记忆翻转卡牌游戏诞生记
状态模式
Elieal2 天前
Spring MVC 全局异常处理实战
spring·mvc·状态模式
Elieal2 天前
统一 JSON 格式,JacksonObjectMapper 定制 Spring Boot JSON 转换规则
spring boot·json·状态模式
前端不太难2 天前
HarmonyOS PC 应用,先做文档模型
华为·状态模式·harmonyos
前端不太难2 天前
HarmonyOS 走向 PC,应用模型正在重构
重构·状态模式·harmonyos
进击的小头3 天前
行为型模式:状态模式——嵌入式状态管理的优雅解决方案
c语言·状态模式