SpringBoot+Vue+kkFileView实现文档管理(文档上传下载在线预览)
Posted 霸道流氓气质
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot+Vue+kkFileView实现文档管理(文档上传下载在线预览)相关的知识,希望对你有一定的参考价值。
场景
SpringBoot+Vue+OpenOffice实现文档管理(文档上传、下载、在线预览):
SpringBoot+Vue+OpenOffice实现文档管理(文档上传、下载、在线预览)_霸道流氓气质的博客-CSDN博客_vue openoffice
上面在使用OpenOffice实现doc、excel、ppt等文档的管理和预览。
除此之外可用kkFileView实现包括且更多文档的预览。
kkFileView
kkFileView为文件文档在线预览解决方案,该项目使用流行的spring boot搭建,易上手和部署,
基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等。
gitee地址:
kkFileView部署和SpringBoot接入指南
具体参考文档:
这里是windows电脑,所以直接下载发行版本并解压运行即可
解压之后找到bin下bat,双击启动即可
启动成功之后访问:
出现如下界面则成功
项目接入使用
当您的项目内需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url
var url = 'http://127.0.0.1:8080/file/test.txt'; //要预览文件的访问地址
window.open('http://127.0.0.1:8012/onlinePreview?url='+encodeURIComponent(base64Encode(url)));
注:
博客:
霸道流氓气质的博客_CSDN博客-C#,架构之路,SpringBoot领域博主
关注公众号
霸道的程序猿
获取编程相关电子书、教程推送与免费下载。
实现
建表与后台SpringBoot代码生成
若依前后端分离版本地搭建开发环境并运行项目的教程:
若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客_运行若依分离版
基于上面搭建起来架构的基础上。
设计数据库表
DROP TABLE IF EXISTS `bus_file_preview`;
CREATE TABLE `bus_file_preview` (
`id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '主键',
`file_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件名',
`preview_server_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '预览服务地址',
`file_path` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件磁盘路径',
`file_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '文件url',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`create_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '创建人',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`update_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '更新人',
`remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '文件上传与预览' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
表结构
使用若依自带代码生成工具生成代码,下面展示部分代码
实体类BusFilePreview
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
public class BusFilePreview extends BaseEntity
private static final long serialVersionUID = 1L;
/** 主键 */
private Long id;
/** 文件名 */
@Excel(name = "文件名")
private String fileName;
/** 预览服务地址 */
@Excel(name = "预览服务地址")
private String previewServerUrl;
/** 文件url */
@Excel(name = "文件url")
private String fileUrl;
/** 文件磁盘路径 */
@Excel(name = "文件磁盘路径")
private String filePath;
public void setId(Long id)
this.id = id;
public Long getId()
return id;
public void setFileName(String fileName)
this.fileName = fileName;
public String getFileName()
return fileName;
public void setPreviewServerUrl(String previewServerUrl)
this.previewServerUrl = previewServerUrl;
public String getPreviewServerUrl()
return previewServerUrl;
public void setFileUrl(String fileUrl)
this.fileUrl = fileUrl;
public String getFileUrl()
return fileUrl;
public void setFilePath(String filePath)
this.filePath = filePath;
public String getFilePath()
return filePath;
@Override
public String toString()
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("fileName", getFileName())
.append("previewServerUrl", getPreviewServerUrl())
.append("fileUrl", getFileUrl())
.append("createTime", getCreateTime())
.append("createBy", getCreateBy())
.append("updateTime", getUpdateTime())
.append("updateBy", getUpdateBy())
.append("remark", getRemark())
.toString();
文件上传功能实现
前端添加el-upload
<!-- 添加或修改preview对话框 -->
<el-dialog :title="title" :visible.sync="open" width="35%" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="文件名" prop="fileName">
<el-input
v-model="form.fileName"
placeholder="请输入文件名"
disabled
/>
</el-form-item>
<el-form-item label="附件" prop="photoPath">
<el-upload
:headers="headers"
:action="url"
:multiple="false"
:file-list="fileList"
:on-remove="fileRemove"
:on-success="uploadSuccess"
:on-error="uploadError"
:on-progress="uploadProgress"
:before-upload="beforeUpload"
:limit="1"
:on-exceed="beyond"
>
<el-button size="small">
上传
<i class="el-icon-upload el-icon--right"></i>
</el-button>
<div class="el-upload__tip" style="color: red" slot="tip">
提示:仅允许导入".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
".pdf", ".mp3",".mp4",".wav"格式文件!
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
并设置各回调事件,事件实现
// 文件上传失败
uploadError(err)
this.btnLoding = false;
this.$message.error(res.msg);
,
// 上传中
uploadProgress(e)
this.btnLoding = true;
,
// 文件上传之前
beforeUpload(file)
const fileName = file.name;
const fileType = fileName.substring(fileName.lastIndexOf("."));
const whiteList = [
".doc",
".docx",
".xls",
".xlsx",
".ppt",
".pptx",
".pdf",
".mp3",
".mp4",
".wav",
];
//array.indexOf此方法判断数组中是否存在某个值,如果存在返回数组元素的下标,否则返回-1。
if (whiteList.indexOf(fileType) === -1)
this.$message.error("只允许如下文件类型:" + whiteList.toString());
return false;
// 不处理
else
this.form.fileName = file.name;
,
// 文件上传成功
uploadSuccess(res, file, fileList)
this.form.previewServerUrl = res.previewServerUrl;
this.form.fileUrl = res.fileUrl;
this.form.filePath = res.filePath;
this.btnLoding = false;
this.fileList = fileList;
this.$message(res.msg);
,
beyond(file, fileList)
this.$message(
message: "最多上传一个文件",
type: "warning",
);
,
// 移除选择的文件
fileRemove(file, fileList)
this.btnLoding = false;
this.reset();
this.fileList = [];
,
重点关注文件上传之前的beforeUpload和文件上传成功的uploadSuccess
上传之前获取文件名和后缀名,然后判断是否为允许的文件类型,如果允许,获取文件名。
文件上传成功之后获取后台接口返回的文件预览服务地址和文件预览url以及磁盘路径三个参数。
其中文件预览服务地址指的是kkFileView的ip和端口号以及预览的url,比如这里是与后台服务放在了一起,
所以previewServerUrl为http://127.0.0.1:8012/onlinePreview?url=
文件预览url为调用kkFileView预览时传递的参数,比如
http://127.0.0.1:9090/profile/upload/2022/12/10/a28ffa19-9982-42d2-8766-1feb274c5bb7.doc
文件磁盘路径映射为网络url可以参考
SpringBoot中通过重写WebMvcConfigurer的方法配置静态资源映射实现图片上传后返回网络Url:
SpringBoot中通过重写WebMvcConfigurer的方法配置静态资源映射实现图片上传后返回网络Url_霸道流氓气质的博客-CSDN博客
这里后台将预览文件的网络url单独返回,是因为在前端进行预览时,需要对url参数进行base64编码
如果不进行编码预览会有问题,会提示
Illegal base64 character 3a
这点官网有说明
SpringBoot上传接口实现
代码实现
@PostMapping("/uploadPreviewFile")
public AjaxResult uploadPreviewFile(@Param("file") MultipartFile file)
AjaxResult ajaxResult = AjaxResult.success();
try
//D:/ruoyi/uploadPath/upload/2022/12/10/
String path = RuoYiConfig.getUploadPath() + "/" + DateUtils.datePath() + "/";
FileUtils.check_folder(path);
// 上传后的文件名称
//423208ab-2171-4631-9e08-382c00aacc43.doc
String auth_file_name = UploadUtil.save_file_withAllow(file, path ,allowFiles);
if (StringUtils.isEmpty(auth_file_name))
return AjaxResult.error(HttpStatus.BAD_REQUEST, "文件格式不合法");
ajaxResult.put("code", 200);
ajaxResult.put("message", "成功");
ajaxResult.put("fileName", auth_file_name);
ajaxResult.put("filePath", path + auth_file_name);
//serverConfig.getUrl() http://127.0.0.1:9090
//Constants.RESOURCE_PREFIX /profile
//RuoYiConfig.getUploadPathPre() /upload
//File.separator /
//DateUtils.datePath() /2022/12/10
//auth_file_name a28ffa19-9982-42d2-8766-1feb274c5bb7.doc
//url http://127.0.0.1:9090/profile/upload/2022/12/10/a28ffa19-9982-42d2-8766-1feb274c5bb7.doc
String fileUrl = serverConfig.getUrl()+ Constants.RESOURCE_PREFIX + RuoYiConfig.getUploadPathPre() + File.separator + DateUtils.datePath() + File.separator + auth_file_name;
String previewServerUrl = Constants.HTTP + previewConfig.getServerIp() +":" +previewConfig.getServerPort() + Constants.PREVIEW_SERVER_URL;
ajaxResult.put("previewServerUrl", previewServerUrl);
ajaxResult.put("fileUrl", fileUrl);
catch (IOException e)
ajaxResult.put("code", 400);
ajaxResult.put("message", "上传失败");
e.printStackTrace();
return ajaxResult;
为方便更清晰的调试理解,这里在生成网络url时标注了每一步的参数
首先调用若依的工具类获取并检查文件上传目录,这里的RuoYiConfig.getUploadPath()配置的是D:/ruoyi/uploadPath
然后最终path的值为D:/ruoyi/uploadPath/upload/2022/12/10/
然后调用文件上传方法,这里新建了一个重载方法,传递了允许上传的文件类型,若依自带的方法该参数是写死的
public static String save_file_withAllow(MultipartFile file, String path ,String[] allowFiles) throws IOException
String filename=file.getOriginalFilename();
//后缀名校验
String suffix = filename.substring(filename.indexOf("."));
List<String> allowFileList = new ArrayList<>(Arrays.asList(allowFiles));
if (!allowFileList.contains(suffix))
return null;
filename = UUID.randomUUID().toString() + suffix;
File file_temp=new File(path,filename);
if (!file_temp.getParentFile().exists())
file_temp.getParentFile().mkdir();
if (file_temp.exists())
file_temp.delete();
file_temp.createNewFile();
file.transferTo(file_temp);
return file_temp.getName();
允许上传的格式需要声明
/**允许上传的格式*/
private static String[] allowFiles = ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf",".mp3",".mp4",".wav"
;
上传成功之后获取到文件的文件名,比如423208ab-2171-4631-9e08-382c00aacc43.doc
然后生成文件预览网络url
//serverConfig.getUrl() http://127.0.0.1:9090
//Constants.RESOURCE_PREFIX /profile
//RuoYiConfig.getUploadPathPre() /upload
//File.separator /
//DateUtils.datePath() /2022/12/10
//auth_file_name a28ffa19-9982-42d2-8766-1feb274c5bb7.doc
//url http://127.0.0.1:9090/profile/upload/2022/12/10/a28ffa19-9982-42d2-8766-1feb274c5bb7.doc
String fileUrl = serverConfig.getUrl()+ Constants.RESOURCE_PREFIX + RuoYiConfig.getUploadPathPre() + File.separator + DateUtils.datePath() + File.separator + auth_file_name;
这里的serverConfig是若依自带获取服务相关配置,其它的就是一些常量配置。
RuoYiConfig.getUploadPathPre() 是自己新增的获取上传路径的前缀
/**
* 获取上传路径前缀
*/
public static String getUploadPathPre()
return "/upload";
最后返回kkFileView服务预览的ip和端口以及前缀
String previewServerUrl = Constants.HTTP + previewConfig.getServerIp() +":" +previewConfig.getServerPort() + Constants.PREVIEW_SERVER_URL;
这里将ip和端口写在配置文件中
#文件预览相关配置
preview:
serverIp: 127.0.0.1
serverPort: 8012
然后新增配置文件获取
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "preview")
public class PreviewConfig
private String serverIp;
private String serverPort;
public String getServerIp()
return serverIp;
public void setServerIp(String serverIp)
this.serverIp = serverIp;
public String getServerPort()
return serverPort;
public void setServerPort(String serverPort)
this.serverPort = serverPort;
最终返回的previewServerUrl为
http://127.0.0.1:8012/onlinePreview?url=
上传成功之后,前端获取返回参数并赋值到form中,前端点击提交按钮时
/** 提交按钮 */
submitForm()
this.$refs["form"].validate((valid) =>
if (valid)
if (this.form.id != null)
updatePreview(this.form).then((response) =>
this.msgSuccess("修改成功");
this.open = false;
this.fileList = [];
this.getList();
);
else
addPreview(this.form).then((response) =>
this.msgSuccess("新增成功");
this.open = false;
this.fileList = [];
this.getList();
);
);
,
将数据存储到数据库中。
文件上传效果实现
文件下载实现
前端下载按钮点击事件
// 下载
handleDownload(row)
var filePath = row.filePath;
var fileName = row.fileName;
var url =
this.downloadUrl + "?filePath=" + filePath + "&fileName=" + fileName;
const a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("target", "_blank");
a.setAttribute("href", url);
a.click();
,
获取文件磁盘路径和文件名,并传递参数
后台SpringBoot接口
@GetMapping("download")
@ApiOperation("下载")
public void downloadFile(String filePath,String fileName, HttpServletResponse response) throws IOException
File file = new File(filePath);
// 清空response
response.reset();
// 设置response的Header 通知浏览器 已下载的方式打开文件 防止文本图片预览
response.addHeader("Content-Disposition",
"attachment;filename=" + new String(fileName.getBytes("gbk"), "iso-8859-1")); // 转码之后下载的文件不会出现中文乱码
response.addHeader("Content-Length", "" + file.length());
// 以流的形式下载文件
InputStream fis = new BufferedInputStream(new FileInputStream(filePath));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
toClient.write(buffer);
toClient.flush();
toClient.close();
文件下载效果
预览实现
前端点击预览的点击事件
// 预览
handlePreview(row)
var previewServerUrl = row.previewServerUrl;
var fileUrl = row.fileUrl;
//分别获取预览服务地址和预览参数的地址然后拼接
//预览文件url地址需要使用Base64编码URL
let url =
previewServerUrl + encodeURIComponent(Base64.encodeURI(fileUrl));
window.open(url);
,
注意这里获取预览服务地址和预览参数文件url,这里需要将文件url进行Base64编码
Vue中使用Base64编码
安装依赖
npm install --save js-base64
引入依赖
import Base64 from "js-base64";
调用编码方法
Base64.encodeURI(fileUrl)
预览效果
代码完整示例
前端Vue页面完整示例代码
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryForm"
:inline="true"
v-show="showSearch"
label-width="68px"
>
<el-form-item label="文件名" prop="fileName">
<el-input
v-model="queryParams.fileName"
placeholder="请输入文件名"
clearable
size="small"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button
type="cyan"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['basicinfomanage:preview:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['basicinfomanage:preview:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['basicinfomanage:preview:remove']"
>删除</el-button
>
</el-col>
<right-toolbar
:showSearch.sync="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="previewList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column
show-overflow-tooltip
label="文件名"
align="center"
prop="fileName"
/>
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
width="200"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['basicinfomanage:preview:edit']"
>修改</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handlePreview(scope.row)"
>预览</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleDownload(scope.row)"
>下载</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['basicinfomanage:preview:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改preview对话框 -->
<el-dialog :title="title" :visible.sync="open" width="35%" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="110px">
<el-form-item label="文件名" prop="fileName">
<el-input
v-model="form.fileName"
placeholder="请输入文件名"
disabled
/>
</el-form-item>
<el-form-item label="附件" prop="photoPath">
<el-upload
:headers="headers"
:action="url"
:multiple="false"
:file-list="fileList"
:on-remove="fileRemove"
:on-success="uploadSuccess"
:on-error="uploadError"
:on-progress="uploadProgress"
:before-upload="beforeUpload"
:limit="1"
:on-exceed="beyond"
>
<el-button size="small">
上传
<i class="el-icon-upload el-icon--right"></i>
</el-button>
<div class="el-upload__tip" style="color: red" slot="tip">
提示:仅允许导入".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
".pdf", ".mp3",".mp4",".wav"格式文件!
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import
listPreview,
getPreview,
delPreview,
addPreview,
updatePreview,
from "@/api/basicinfomanage/preview";
import getToken from "@/utils/auth";
import Base64 from "js-base64";
export default
name: "Preview",
data()
return
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// preview表格数据
previewList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams:
pageNum: 1,
pageSize: 10,
fileName: null,
,
// 表单参数
form: ,
// 表单校验
rules:
fileName: [
required: true,
message: "文件名称不能为空",
trigger: "blur",
,
],
,
// 上传按钮闸口
btnLoding: false,
// 请求头
headers: Authorization: "Bearer" + " " + getToken() ,
// 上传地址
url:
process.env.VUE_APP_BASE_API +
"/fzys/basicinfomanage/preview/uploadPreviewFile",
// 下载地址
downloadUrl:
process.env.VUE_APP_BASE_API + "/fzys/basicinfomanage/preview/download",
// 图片列表
fileList: [],
;
,
created()
this.getList();
,
methods:
/** 查询preview列表 */
getList()
this.loading = true;
listPreview(this.queryParams).then((response) =>
this.previewList = response.rows;
this.total = response.total;
this.loading = false;
);
,
// 取消按钮
cancel()
this.open = false;
this.reset();
,
// 表单重置
reset()
this.form =
id: null,
fileName: null,
;
this.resetForm("form");
,
/** 搜索按钮操作 */
handleQuery()
this.queryParams.pageNum = 1;
this.getList();
,
/** 重置按钮操作 */
resetQuery()
this.resetForm("queryForm");
this.handleQuery();
,
// 多选框选中数据
handleSelectionChange(selection)
this.ids = selection.map((item) => item.id);
this.single = selection.length !== 1;
this.multiple = !selection.length;
,
/** 新增按钮操作 */
handleAdd()
this.fileRemove();
this.open = true;
this.title = "添加文件";
,
/** 修改按钮操作 */
handleUpdate(row)
this.reset();
const id = row.id || this.ids;
getPreview(id).then((response) =>
this.form = response.data;
this.open = true;
this.title = "修改文件";
);
,
// 预览
handlePreview(row)
var previewServerUrl = row.previewServerUrl;
var fileUrl = row.fileUrl;
//分别获取预览服务地址和预览参数的地址然后拼接
//预览文件url地址需要使用Base64编码URL
let url =
previewServerUrl + encodeURIComponent(Base64.encodeURI(fileUrl));
window.open(url);
,
// 下载
handleDownload(row)
var filePath = row.filePath;
var fileName = row.fileName;
var url =
this.downloadUrl + "?filePath=" + filePath + "&fileName=" + fileName;
const a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("target", "_blank");
a.setAttribute("href", url);
a.click();
,
/** 提交按钮 */
submitForm()
this.$refs["form"].validate((valid) =>
if (valid)
if (this.form.id != null)
updatePreview(this.form).then((response) =>
this.msgSuccess("修改成功");
this.open = false;
this.fileList = [];
this.getList();
);
else
addPreview(this.form).then((response) =>
this.msgSuccess("新增成功");
this.open = false;
this.fileList = [];
this.getList();
);
);
,
/** 删除按钮操作 */
handleDelete(row)
const ids = row.id || this.ids;
this.$confirm('是否确认删除文件编号为"' + ids + '"的数据项?', "警告",
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
)
.then(function ()
return delPreview(ids);
)
.then(() =>
this.getList();
this.msgSuccess("删除成功");
);
,
// 文件上传失败
uploadError(err)
this.btnLoding = false;
this.$message.error(res.msg);
,
// 上传中
uploadProgress(e)
this.btnLoding = true;
,
// 文件上传之前
beforeUpload(file)
const fileName = file.name;
const fileType = fileName.substring(fileName.lastIndexOf("."));
const whiteList = [
".doc",
".docx",
".xls",
".xlsx",
".ppt",
".pptx",
".pdf",
".mp3",
".mp4",
".wav",
];
//array.indexOf此方法判断数组中是否存在某个值,如果存在返回数组元素的下标,否则返回-1。
if (whiteList.indexOf(fileType) === -1)
this.$message.error("只允许如下文件类型:" + whiteList.toString());
return false;
// 不处理
else
this.form.fileName = file.name;
,
// 文件上传成功
uploadSuccess(res, file, fileList)
this.form.previewServerUrl = res.previewServerUrl;
this.form.fileUrl = res.fileUrl;
this.form.filePath = res.filePath;
this.btnLoding = false;
this.fileList = fileList;
this.$message(res.msg);
,
beyond(file, fileList)
this.$message(
message: "最多上传一个文件",
type: "warning",
);
,
// 移除选择的文件
fileRemove(file, fileList)
this.btnLoding = false;
this.reset();
this.fileList = [];
,
,
;
</script>
后台Controller完整代码
package com.ruoyi.web.controller.fzys.basicinfomannager;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.fzys.basicinfomanage.BusFilePreview;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.UploadUtil;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.fzys.service.basicinfomanageService.IBusFilePreviewService;
import com.ruoyi.system.utils.FileUtils;
import com.ruoyi.web.controller.fzys.config.PreviewConfig;
import io.swagger.annotations.ApiOperation;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
/**
* previewController
*
* @author ruoyi
* @date 2021-10-29
*/
@RestController
@RequestMapping("/fzys/basicinfomanage/preview")
public class BusFilePreviewController extends BaseController
/**允许上传的格式*/
private static String[] allowFiles = ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf",".mp3",".mp4",".wav"
;
@Autowired
private ServerConfig serverConfig;
@Autowired
private PreviewConfig previewConfig;
@Autowired
private IBusFilePreviewService busFilePreviewService;
@PostMapping("/uploadPreviewFile")
public AjaxResult uploadPreviewFile(@Param("file") MultipartFile file)
AjaxResult ajaxResult = AjaxResult.success();
try
String path = RuoYiConfig.getUploadPath() + "/" + DateUtils.datePath() + "/";
FileUtils.check_folder(path);
String auth_file_name = UploadUtil.save_file_withAllow(file, path ,allowFiles);
if (StringUtils.isEmpty(auth_file_name))
return AjaxResult.error(HttpStatus.BAD_REQUEST, "文件格式不合法");
ajaxResult.put("code", 200);
ajaxResult.put("message", "成功");
ajaxResult.put("fileName", auth_file_name);
ajaxResult.put("filePath", path + auth_file_name);
String fileUrl = serverConfig.getUrl()+ Constants.RESOURCE_PREFIX + RuoYiConfig.getUploadPathPre() + File.separator + DateUtils.datePath() + File.separator + auth_file_name;
String previewServerUrl = Constants.HTTP + previewConfig.getServerIp() +":" +previewConfig.getServerPort() + Constants.PREVIEW_SERVER_URL;
ajaxResult.put("previewServerUrl", previewServerUrl);
ajaxResult.put("fileUrl", fileUrl);
catch (IOException e)
ajaxResult.put("code", 400);
ajaxResult.put("message", "上传失败");
e.printStackTrace();
return ajaxResult;
/**
* 下载文件
* @param fileName
* @param response
* @throws IOException
*/
@GetMapping("download")
@ApiOperation("下载")
public void downloadFile(String filePath,String fileName, HttpServletResponse response) throws IOException
File file = new File(filePath);
// 清空response
response.reset();
// 设置response的Header 通知浏览器 已下载的方式打开文件 防止文本图片预览
response.addHeader("Content-Disposition",
"attachment;filename=" + new String(fileName.getBytes("gbk"), "iso-8859-1")); // 转码之后下载的文件不会出现中文乱码
response.addHeader("Content-Length", "" + file.length());
// 以流的形式下载文件
InputStream fis = new BufferedInputStream(new FileInputStream(filePath));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
toClient.write(buffer);
toClient.flush();
toClient.close();
/**
* 查询preview列表
*/
@GetMapping("/list")
public TableDataInfo list(BusFilePreview busFilePreview)
startPage();
List<BusFilePreview> list = busFilePreviewService.selectBusFilePreviewList(busFilePreview);
return getDataTable(list);
/**
* 导出preview列表
*/
@Log(title = "preview", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, BusFilePreview busFilePreview) throws IOException
List<BusFilePreview> list = busFilePreviewService.selectBusFilePreviewList(busFilePreview);
ExcelUtil<BusFilePreview> util = new ExcelUtil<BusFilePreview>(BusFilePreview.class);
util.exportExcel(response, list, "preview");
/**
* 获取preview详细信息
*/
@GetMapping(value = "/id")
public AjaxResult getInfo(@PathVariable("id") Long id)
return AjaxResult.success(busFilePreviewService.selectBusFilePreviewById(id));
/**
* 新增preview
*/
@Log(title = "preview", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@RequestBody BusFilePreview busFilePreview) throws IOException
if (StringUtils.isNull(busFilePreview.getFileName()))
AjaxResult.error("缺少文件名称");
return toAjax(busFilePreviewService.insertBusFilePreview(busFilePreview));
/**
* 修改preview
*/
@Log(title = "preview", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@RequestBody BusFilePreview busFilePreview)
return toAjax(busFilePreviewService.updateBusFilePreview(busFilePreview));
/**
* 删除preview
*/
@Log(title = "preview", businessType = BusinessType.DELETE)
@DeleteMapping("/ids")
public AjaxResult remove(@PathVariable Long[] ids)
return toAjax(busFilePreviewService.deleteBusFilePreviewByIds(ids));
后台其他各层代码均为根据表结构代码生成。
问题
1、注意后台需要放开下载接口的鉴权
2、如果在预览时页面显示
Whitelabel Error Page
找到kkFileView目录下log下kkFileView.log文件查看具体报错
Illegal base64 character 3a
这是因为一开始没将预览文件url进行Base64编码导致。
3、上传文件时提示
Maximum upload size exceeded:nested exception is java.lang.lllegalStateException:
org.apache.tomcat.util.http.fileupload.FileSizeLimitExceeededException:
The fiel filed exceeds its maximum perrmitted size of xxx bytes
找到application.yml中修改如下配置
# Spring配置
spring:
# 资源信息
messages:
# 国际化资源文件路径
basename: i18n/messages
profiles:
active: druid
# 文件上传
servlet:
multipart:
# 单个文件大小
max-file-size: 100MB
# 设置总上传的文件大小
max-request-size: 200MB
修改位置
基于SpringBoot的文件在线预览
基于SpringBoot的文件在线预览
kkFileView简介
kkFileView可以用来搭建文件在线预览服务,该项目使用流行的SpringBoot搭建,易上手和部署,基本支持主流办公文档的在线预览,如docx、xlsx、pptx、pdf、txt、zip、图片、视频、音频等等。项目特性可以参考下图:
kkFileView支持在Windows和Linux下安装,下面我们介绍下它的安装,基本就是开箱即用!
Windows安装
1.首先下载最新版的安装包,下载地址:https://gitee.com/kekingcn/file-online-preview/releases
2.下载成功后,解压到指定目录,然后运行bin目录下的startup.bat命令即可;
3.由于是SpringBoot项目,想要修改配置的话,只需修改config目录下的application.properties文件即可。
4.运行成功后,可以访问kkfileview的测试页,地址:http://localhost:8012
Linux安装
在Linux下使用Docker安装非常简单,只需两个命令即可,首先下载kkFileView的Docker镜像:
docker pull keking/kkfileview
下载完成后运行kkfileview的Docker容器,服务将运行在8012端口上:
docker run -p 8012:8012 --name kkfileview \\
-d keking/kkfileview
运行成功后,可以访问kkfileview的测试页,地址地址:http://106.14.156.185:8012
kkfileview和MinIO结合使用
MinIO的使用请参考:https://blog.csdn.net/muriyue6/article/details/119643542
1.我们可以使用S3 Browser来管理MinIO中的文件,首先创建一个存储桶为preview,然后将文件都上传上去;
2.上传成功后需要修改存储桶的访问策略,让匿名用户可以访问;
3.修改策略时直接参考Policy Examples即可,注意修改下version为2012-10-17;
4.修改成功后就可以在线预览文件了,我们来看下在线预览文件的URL格式,只需传入url(需要预览的文件地址)即可,这里要注意的是这个url需要进行base64编码才可以;
http://106.14.156.185:8012/onlinePreview?url=base64Encode($url)
5.接下来我们来预览个图片试试,首先获取图片的访问地址;
6.然后找个网站把url进行base64编码,我使用的是这个:https://tool.oschina.net/encrypt?type=3
7.接下来把url参数放入访问路径中即可预览图片了,是不是很简单!
8.再来预览下word文档,我们可以发现右侧有个JPG的按钮,可以切换到JPG试图,其实kkfileview是通过把word文档转换为pdf或者JPG来实现文档预览的
9.再来预览下纯文本的Markdown文档;
kkfileview的配置
kkfileview的配置众多,具体直接参考config目录下的application.properties文件即可,这里介绍下如何自定义Docker容器中的配置。
1.如果你想修改kkfileview的配置文件的话,可以先从Docker容器中拷贝出配置文件目录;
docker cp kkfileview:/opt/kkFileView-3.5.1/config /home/kkFileView/
2.然后将配置目录、文件目录、日志目录挂载到宿主机并运行;
docker run -p 8012:8012 --name kkfileview \\
-v /home/kkFileView/config:/opt/kkFileView-3.5.1/config \\
-v /home/kkFileView/file:/opt/kkFileView-3.5.1/file \\
-v /home/kkFileView/log:/opt/kkFileView-3.5.1/log \\
-d keking/kkfileview
3.如果我们想要给预览文件添加水印的话,可以修改application.properties文件中的如下配置,比如我想添加macrozheng这个水印;
#水印内容
#如需取消水印,内容设置为空即可,例:watermark.txt = ${WATERMARK_TXT:}
watermark.txt = ${WATERMARK_TXT:muriyue}
4.修改成功后,再次预览图片就会发现水印已经添加了。
以上是关于SpringBoot+Vue+kkFileView实现文档管理(文档上传下载在线预览)的主要内容,如果未能解决你的问题,请参考以下文章