2021SC@SDUSC
项目介绍
项目简介
此项目为文件文档在线预览项目解决方案,对标业内付费产品有【永中office】【office365】【idocv】等,在取得公司高层同意后以Apache协议开源出来反哺社区,
项目地址: kkFileView: 使用spring boot打造文件文档在线预览项目解决方案,支持doc、docx、ppt、pptx、xls、xlsx、zip、rar、mp4、mp3以及众多类文本如txt、html、xml、java、properties、sql、js、md、json、conf、ini、vue、php、py、bat、gitignore等文件在线预览
项目特性
2. 部署运行
1). 物理机或虚拟机上运行
2). Docker容器环境环境运行
docker pull keking/kkfileview
docker run -it -p 8012:8012 keking/kkfileview
3. 项目接入使用
当您的项目内需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url,示例如下:
详细使用说明见 kkFileView使用指南
使用指南
当您的项目内需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url。
注意,如果要预览的url里包含需要转义的特殊字符,如下表格,请使用encodeURIComponent(url)转义
符号 | url中转义结果 | 转义码 |
---|---|---|
+ | URL 中+号表示空格 | %2B |
空格 | URL中的空格可以用+号或者编码 | %20 |
/ | 分隔目录和子目录 | %2F |
? | 分隔实际的URL和参数 | %3F |
% | 指定特殊字符 | %25 |
# | 表示书签 | %23 |
& | URL 中指定的参数间的分隔符 | %26 |
= | URL 中指定参数的值 | %3D |
1. 普通文件下载url预览
2. http/https下载流url预览
很多系统内不是直接暴露文件下载地址,而是请求通过id、code等参数到通过统一的接口,后端通过id或code等参数定位文件,再通过OutputStream输出下载,此时下载url是不带文件后缀名的,预览时需要拿到文件名,传一个参数fullfilename=xxx.xxx
来指定文件名,示例如下
3. FTP下载url预览
1). FTP匿名访问
如果要预览的FTP url是可以匿名访问的(不需要用户名密码),则可以直接通过下载url预览,示例如下
2). 非匿名访问,确定只有访问一台FTP服务器
如果预览服务只访问一台FTP服务器,只需要在配置文件中 配置FTP链接信息 ,则可以直接通过下载url预览,示例如下
3). 非匿名访问,不只访问一台FTP服务器
如果预览服务要访问多台FTP服务器,且用户名密码不一致,可以通过在url中加入用户名密码等参数预览(url参数中的优化于配置文件中的),示例如下,
- 支持word excel ppt,pdf等办公文档
- 支持txt,java,php,py,md,js,css等所有纯文本
- 支持zip,rar,jar,tar,gzip等压缩包
- 支持jpg,jpeg,png,gif等图片预览(翻转,缩放,镜像)
- 支持mp3,mp4,flv等多媒体文件预览
- 使用spring boot开发,预览服务搭建部署非常简便
- rest接口提供服务,跨平台特性(java,php,python,go,php,....)都支持,应用接入简单方便
- 支持普通http/https文件下载url、http/https文件下载流url、ftp下载url等多种预览源
- 提供zip,tar.gz发行包,提供一键启动脚本和丰富的配置项,方便部署使用
- 提供Docker镜像发行包,方便在容器环境部署
- 抽象预览服务接口,方便二次开发,非常方便添加其他类型文件预览支持
- 最最重要Apache协议开源,代码pull下来想干嘛就干嘛
-
部署指南
1. 环境要求 - Java: 1.8+
- OpenOffice或LiberOffice(Windows下已内置,CentOS或Ubuntu下会自动下载安装,MacOS下需要自行安装)
- 从 码云发行版本 下载最新版发行包
- 解压kkFileView-2.x.x.zip包
- 打开解压后文件夹的bin目录,运行startup脚本(Windows下以管理员身份运行
startup.bat
,Linux以root用户运行startup.sh
) - 浏览器访问本机8012端口(http://127.0.0.1:8012 )即可看到项目演示用首页
- 拉取镜像
- 运行
- 浏览器访问容器8012端口(http://xxx.xxx.xxx.xxx:8012 )即可看到项目演示用首页
-
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(previewUrl));
-
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url));
-
var originUrl = 'http://127.0.0.1:8080/filedownload?fileId=1'; //要预览文件的访问地址
-
var previewUrl = originUrl + '&fullfilename=test.txt'
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(previewUrl));
-
var url = 'ftp://127.0.0.1/file/test.txt'; //要预览文件的访问地址
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url));
-
var url = 'ftp://127.0.0.1/file/test.txt'; //要预览文件的访问地址
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(url));
-
var originUrl = 'ftp://127.0.0.1/file/test.txt'; //要预览文件的访问地址
-
var previewUrl = originUrl + '?ftp.username=xx&ftp.password=xx&ftp.control.encoding=xx';
-
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(previewUrl));
核心代码:
package cn.keking.web.controller; import cn.keking.config.ConfigConstants; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import cn.keking.model.ReturnResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; /** * * @author yudian-it * @date 2017/12/1 */ @RestController public class FileController { private final Logger logger = LoggerFactory.getLogger(FileController.class); private final String fileDir = ConfigConstants.getFileDir(); private final String demoDir = "demo"; private final String demoPath = demoDir + File.separator; @RequestMapping(value = "fileUpload", method = RequestMethod.POST) public String fileUpload(@RequestParam("file") MultipartFile file) throws JsonProcessingException { // 获取文件名 String fileName = file.getOriginalFilename(); //判断是否为IE浏览器的文件名,IE浏览器下文件名会带有盘符信息 // Check for Unix-style path int unixSep = fileName.lastIndexOf('/'); // Check for Windows-style path int winSep = fileName.lastIndexOf('\\'); // Cut off at latest possible point int pos = (Math.max(winSep, unixSep)); if (pos != -1) { fileName = fileName.substring(pos + 1); } // 判断是否存在同名文件 if (existsFile(fileName)) { return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null)); } File outFile = new File(fileDir + demoPath); if (!outFile.exists()) { outFile.mkdirs(); } logger.info("上传文件:{}", fileDir + demoPath + fileName); try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) { StreamUtils.copy(in, out); return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null)); } catch (IOException e) { logger.error("文件上传失败", e); return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null)); } } @RequestMapping(value = "deleteFile", method = RequestMethod.GET) public String deleteFile(String fileName) throws JsonProcessingException { if (fileName.contains("/")) { fileName = fileName.substring(fileName.lastIndexOf("/") + 1); } File file = new File(fileDir + demoPath + fileName); logger.info("删除文件:{}", file.getAbsolutePath()); if (file.exists()) { file.delete(); } return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null)); } @RequestMapping(value = "listFiles", method = RequestMethod.GET) public String getFiles() throws JsonProcessingException { List<Map<String, String>> list = Lists.newArrayList(); File file = new File(fileDir + demoPath); if (file.exists()) { Arrays.stream(Objects.requireNonNull(file.listFiles())).forEach(file1 -> list.add(ImmutableMap.of("fileName", demoDir + "/" + file1.getName()))); } return new ObjectMapper().writeValueAsString(list); } private boolean existsFile(String fileName) { File file = new File(fileDir + demoPath + fileName); return file.exists(); } }