Snapan项目--预览文件梳理
整体思路
前端触发预览请求--->后端生成安全预览链接--->前端按文件类型适配渲染--->关闭预览
触发预览、前端请求预览链接
功能说明
用户点击文件预览按钮后,前端展示预览模态框并初始化加载状态。
核心代码【home.html】
动态生成文件列表时:
<div class="action-item" onclick="previewFile('${file.filePath}')" data-tooltip="预览"><button class="action-icon preview">👁️</button></div>
<!-- 预览模态框 --><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()">×</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接口,传递文件路径参数,处理成功/失败响应;
// 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,并提取文件类型。
核心代码
/*** 获取文件预览的签名url* @param filePath oss文件中的路径* @return 包含url和文件类型的json*/@GetMapping("/getPreviewUrl")@ResponseBodypublic 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();}
/*** 生成带签名的预览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 + "\"");}//生成带响应头配置的签名urlcom.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 标签渲染预览内容。
/*** 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 = `<iframesrc="${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 编码避免格式错误;
关闭预览
// 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
