# 文件预览 **Repository Path**: wb04307201/file-view ## Basic Information - **Project Name**: 文件预览 - **Description**: 一个轻量级文件预览的starter组件,支持多种文档和媒体格式的在线预览,采用模块化架构,易于扩展和定制 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 185 - **Forks**: 50 - **Created**: 2022-11-03 - **Last Updated**: 2025-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: 预览, 文档, Spring, OnlyOffice, minio ## README # File View 文件预览
English | 中文
> 一个轻量级文件预览的starter组件,支持多种文档和媒体格式的在线预览,采用模块化架构,易于扩展和定制。 [![](https://jitpack.io/v/com.gitee.wb04307201/file-view.svg)](https://jitpack.io/#com.gitee.wb04307201/file-view) [![star](https://gitee.com/wb04307201/file-view/badge/star.svg?theme=dark)](https://gitee.com/wb04307201/file-view) [![fork](https://gitee.com/wb04307201/file-view/badge/fork.svg?theme=dark)](https://gitee.com/wb04307201/file-view) [![star](https://img.shields.io/github/stars/wb04307201/file-view)](https://github.com/wb04307201/file-view) [![fork](https://img.shields.io/github/forks/wb04307201/file-view)](https://github.com/wb04307201/file-view) ![MIT](https://img.shields.io/badge/License-Apache2.0-blue.svg) ![JDK](https://img.shields.io/badge/JDK-17+-green.svg) ![SpringBoot](https://img.shields.io/badge/Spring%20Boot-3+-green.svg) --- ## 支持的文件类型 - office文件(docx,xlsx,xls,pptx) - 业务流程管理文件(bpmn,dmn,cmmn) - 图片文件 - 视频文件 - 音频文件 - 文档文件(pdf,ofd,epub) - 文本文件/代码文件(sh,c,cpp,cs,css,diff,go,graphql,ini,java,js,json,kt,less,lua,mk,m,pl,php,phtml,txt,py,pyrepl,r,rb,rs,scss,sh,sql,swift,ts,vb,wasm,xml,yaml,yml) - Markdown文档文件 - 3D模型文件(3dm,3ds,3mf,amf,bim,brep,dae,fbx,fcstd,gltf,ifc,iges,step,stl,obj,off,ply,wrl) - 思维导图文件(xmind) - 压缩文件(zip) - CAD(dwg,dxf) ## 引入 ### 增加 JitPack 仓库 ```xml jitpack.io https://jitpack.io ``` ### Maven依赖 ```xml com.gitee.wb04307201.file-view file-view-spring-boot-starter 1.3.1 ``` ### 配置文件 ```yaml file: view: ## 以下为默认的处理器,默认无需配置 bpmn: enable: true dmn: enable: true cmmn: enable: true code: enable: true epub: enable: true image: enable: true markdown: enable: true pdf: enable: true xmind: enable: true ofd: enable: true docx: enable: true excel: enable: true pptx: enable: true o3d: enable: true zip: enable: true cod: enable: true ## 以下为默认的文件名和处理器匹配规则,默认无需配置 strategies: - syntaxAndPattern: glob:*.bpmn serviceName: bpmn - syntaxAndPattern: glob:*.dmn serviceName: dmn - syntaxAndPattern: glob:*.cmmn serviceName: cmmn - syntaxAndPattern: glob:*.{sh,c,cpp,cs,css,diff,go,graphql,ini,java,js,json,kt,less,lua,mk,m,pl,php,phtml,html,txt,py,pyrepl,r,rb,rs,scss,sh,sql,swift,ts,vb,wasm,xml,yaml,yml} serviceName: code - syntaxAndPattern: glob:*.epub serviceName: epub - syntaxAndPattern: glob:*.{jpg,png,bmp,gif,tiff,webp,svg,raw,heic,cr2,nef,orf,sr2} serviceName: image - syntaxAndPattern: glob:*.md serviceName: markdown - syntaxAndPattern: glob:*.pdf serviceName: pdf - syntaxAndPattern: glob:*.xmind serviceName: xmind - syntaxAndPattern: glob:*.ofd serviceName: ofd - syntaxAndPattern: glob:*.docx serviceName: docx - syntaxAndPattern: glob:*.{xlsx,xls} serviceName: excel - syntaxAndPattern: glob:*.pptx serviceName: pptx - syntaxAndPattern: glob:*.{3dm,3ds,3mf,amf,bim,brep,dae,fbx,fcstd,gltf,ifc,iges,step,stl,obj,off,ply,wrl} serviceName: o3d - syntaxAndPattern: glob:*.zip serviceName: zip - syntaxAndPattern: glob:*.{dwg,dxf} serviceName: cad ``` `syntaxAndPattern`通过指定语法(如 glob 或 regex)对文件名进行匹配: - glob:`*.txt` - regex:`(.*)\.txt` ## 使用 ### 静态资源库 部分文件类型使用内置渲染器,如:pdf、epub、xmind、zip、image、code、markdown、cmmn、dmn、bpmn等 使用的js库资源从jsDelivr加载,如无法从jsDelivr获取资源,可以添加`file-view-static`将js库本地化 ```xml com.gitee.wb04307201.file-view file-view-static 1.3.1 ``` ### 访问内置界面进行文件上传和预览 访问 `http://localhost:8080/file/view` ![img.png](img.png) ![gif.gif](gif.gif) ### 预览扩展 下面以OnlyOffice为例说明如何扩展预览: 1. 使用docker安装[OnlyOffice文档开发者版](https://api.onlyoffice.com/docs/docs-api/get-started/basic-concepts/) ```bash docker run --name onlyoffice -i -t -d -p 80:80 -e JWT_ENABLED=false -e ALLOW_PRIVATE_IP_ADDRESS=true onlyoffice/documentserver-de ``` 2. 文件预览渲染器扩展 编写`IView.java`接口的实现`OnlyOfficeView.java`: ```java package cn.wubo.file.view.test; import cn.wubo.file.view.preview.IView; import org.springframework.stereotype.Service; import org.springframework.web.servlet.function.ServerRequest; import org.springframework.web.servlet.function.ServerResponse; import java.net.URI; @Service public class OnlyOfficeView implements IView { @Override public String getServiceName() { return "onlyoffice"; } @Override public ServerResponse handle(ServerRequest request) { String id = request.pathVariable("id"); return ServerResponse.temporaryRedirect(URI.create(String.format("/onlyoffice.html?id=%s",id))).build(); } } ``` 编写页面`onlyoffice.html`: ```html onlyoffice
``` 3. 修改配置,关闭重复的渲染器,重定义文件匹配规则`application.yml`: ```yaml file: view: docx: enable: false excel: enable: false pptx: enable: false strategies: - syntaxAndPattern: glob:*.{docx,doc,xlsx,xls,pptx,ppt} serviceName: onlyoffice ``` 预览效果如下: ![gif_1.gif](gif_1.gif) ### 文件存储扩展 下面以MinIO为例说明如何扩展文件存储: 1. 使用docker安装MinIO: ```bash docker run -p 9000:9000 -p 9001:9001 --name minio -e "MINIO_ROOT_USER=ROOTUSER" -e "MINIO_ROOT_PASSWORD=CHANGEME123" quay.io/minio/minio server /data --console-address ":9001" ``` 2. 添加MinIO依赖: ```xml io.minio minio 8.6.0 ``` 3. 编写接口`IFileStorage.java`的实现`MinioFileStorageImpl.java`: ```java package cn.wubo.file.view.test; import cn.wubo.file.view.exception.LocalFileStorageException; import cn.wubo.file.view.storage.IFileStorage; import cn.wubo.file.view.storage.dto.FileStorageInfo; import cn.wubo.file.view.utils.VersionUtls; import io.minio.GetObjectArgs; import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.RemoveObjectArgs; import org.springframework.stereotype.Service; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.UUID; @Service public class MinioFileStorageImpl implements IFileStorage { private final MinioClient minioClient; private static final String BUCKET_NAME = "temp"; private static List fileStorageInfos = new ArrayList<>(); public MinioFileStorageImpl() { this.minioClient = new MinioClient.Builder() .endpoint("http://127.0.0.1:9000") .credentials("ROOTUSER", "12345678") .build(); } @Override public FileStorageInfo upload(String fileName, byte[] content, String mimeType) { try { String id = UUID.randomUUID().toString(); String version = VersionUtls.generateContentVersion(content, id); Path filePath = Paths.get(version, fileName); minioClient.putObject( PutObjectArgs.builder() .bucket(BUCKET_NAME) .object(filePath.toString()) .stream(new ByteArrayInputStream(content), content.length, -1) .contentType(mimeType) .build() ); FileStorageInfo fpi = new FileStorageInfo(id, fileName, content.length, mimeType, filePath.toString(), version); fileStorageInfos.add(fpi); return fpi; } catch (NoSuchAlgorithmException | IOException | InvalidKeyException | InvalidResponseException | InsufficientDataException | InternalException | ErrorResponseException | XmlParserException | ServerException e) { throw new LocalFileStorageException(e.getMessage(), e); } } @Override public FileStorageInfo findById(String id) { return fileStorageInfos.stream() .filter(fpi -> fpi.getId().equals(id)) .findAny() .orElseThrow(() -> new LocalFileStorageException("File info not found for id: " + id)); } @Override public List list() { return fileStorageInfos; } @Override public byte[] getContentByLocation(String location) { try { InputStream is = minioClient.getObject( GetObjectArgs.builder() .bucket(BUCKET_NAME) .object(location) .build() ); return is.readAllBytes(); } catch (IOException | InvalidKeyException | InvalidResponseException | NoSuchAlgorithmException | InsufficientDataException | InternalException | ErrorResponseException | XmlParserException | ServerException e) { throw new LocalFileStorageException(e.getMessage(), e); } } @Override public Boolean deleteById(String id) { FileStorageInfo fsi = findById(id); if (fsi != null) { try { minioClient.removeObject( RemoveObjectArgs.builder() .bucket(BUCKET_NAME) .object(fsi.getLocation()) .build() ); fileStorageInfos.remove(fsi); } catch (IOException | InvalidKeyException | InvalidResponseException | NoSuchAlgorithmException | InsufficientDataException | InternalException | ErrorResponseException | XmlParserException | ServerException e) { throw new LocalFileStorageException(e.getMessage(), e); } } return true; } } ``` ## 使用的第三方库 | 文件类型 | 第三方库 | |------------------|--------------------------------------------------------------------------| | office文件 | [vue-office](https://github.com/501351981/vue-office) | | 业务流程管理文件 | [bpmn-io](https://github.com/bpmn-io) | | 图片文件 | [viewerjs](https://github.com/fengyuanchen/viewerjs) | | 文档文件(pdf) | [pdfobject](https://github.com/pipwerks/PDFObject) | | 文档文件(ofd) | [ofd.js](https://github.com/DLTech21/ofd.js) | | 文档文件(epub) | [epub.js](https://github.com/futurepress/epub.js) | | 文本文件/代码文件 | [highlight.js](https://github.com/highlightjs/highlight.js) | | Markdown文档文件 | [vditor](https://github.com/Vanessa219/vditor) | | 3D模型文件 | [Online3DViewer](https://github.com/kovacsv/Online3DViewer) | | 思维导图文件 | [xmind-embed-viewer](https://github.com/xmindltd/xmind-embed-viewer) | | 压缩文件 | [jszip](https://github.com/Stuk/jszip) | | CAD | [CAD-Viewer](https://github.com/mlightcad/cad-viewer) |