SpringBoot+Vue+kkFileView实现文档管理(文档上传、下载、在线预览)

news2024/12/30 2:04:44

场景

SpringBoot+Vue+OpenOffice实现文档管理(文档上传、下载、在线预览):

SpringBoot+Vue+OpenOffice实现文档管理(文档上传、下载、在线预览)_霸道流氓气质的博客-CSDN博客_vue openoffice

上面在使用OpenOffice实现doc、excel、ppt等文档的管理和预览。

除此之外可用kkFileView实现包括且更多文档的预览。

kkFileView

kkFileView - 在线文件预览

kkFileView为文件文档在线预览解决方案,该项目使用流行的spring boot搭建,易上手和部署,

基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等。

gitee地址:

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等文件在线预览

kkFileView部署和SpringBoot接入指南

具体参考文档:

文档预览 - Gitee.com

这里是windows电脑,所以直接下载发行版本并解压运行即可

kkFileView 发行版 - Gitee.com

 

解压之后找到bin下bat,双击启动即可

 

启动成功之后访问:

http://127.0.0.1:8012/index

出现如下界面则成功

 

 

项目接入使用

当您的项目内需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的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

修改位置

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux下安装nginx

linux下安装nginx 注&#xff1a;此处需要先安装vmware&#xff0c;下载Centos8等工具&#xff0c;配置好一个虚拟机。 1、下载nginx的linux版本 2、上传至搭建好的linux环境上。 3、解压nginx压缩包 4、安装nginx编译需要的相关 安装 nginx 需要先将官网下载的源码进行编译…

Spring源码深度解析十五:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean

一、前言 文章目录&#xff1a;Spring源码深度解析&#xff1a;文章目录 在上篇中我们概述了Aop 实现的逻辑&#xff0c;但是由于篇幅原因&#xff0c;我们将一部分内容拆成了中篇和下篇内容。本篇即中篇&#xff0c;内容主要是讲述 在 Bean创建过程中Aop 挑选适用于当前Bean…

html简洁风格的个人博客网站模板(源码)

文章目录1.设计来源1.1 博客首界面1.2 个人简介界面1.3 日常记录界面1.4 文章列表界面1.5 文章信息界面2.结构源码2.1 目录结构2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/128288153 html简洁风格…

qtday1:2、重新手动实现对象树模型

代码段加注释 #include <iostream> #include<list> using namespace std; class A { public:list<A *> child; //创建一个链表 public:A(A *parent nullptr){if(parent !nullptr) //说明有父组件{parent->child.push_back(this); //有父组件就将该…

数商云SRM供应商系统询比价采购业务流程介绍,重塑汽修企业核心竞争力

众所周知&#xff0c;采购供应是关系企业经营效益的重要工作。在汽修行业&#xff0c;由于汽车配件的车型规格繁多&#xff0c;技术业务性强&#xff0c;各种类商品采购过程的艰难性、销售状况的复杂性等等&#xff0c;汽配采购往往容易陷入种种困境&#xff0c;极大降低汽修企…

Qt-Web混合开发-QWebEngineView加载网页最小示例(1)

Qt-Web混合开发-QWebEngineView加载网页最小示例&#x1f4a5; 文章目录Qt-Web混合开发-QWebEngineView加载网页最小示例&#x1f4a5;1、概述&#x1f4af;2、实现效果&#x1f4a6;3、实现功能&#x1f4ac;4、关键代码&#x1f4a4;5、源代码&#x1f648;更多精彩内容&…

【20天快速掌握Python】day01-Python入门

1、什么是Python&#xff1f; Python是一门解释型的编程语言&#xff0c;而且是现在世界上最流行的编程语言之一。 2、Python优缺点 优点 简单&#xff1a;Python是一种代表简单主义思想的语言。阅读一个良好的Python程序就感觉像是在读英语一样&#xff0c;尽管这个英语的要…

driftingblues靶机(0ok编码)

环境准备 靶机链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;oylc 虚拟机网络链接模式&#xff1a;桥接模式 攻击机系统&#xff1a;kali linux 2021.1 信息收集 1.探测目标靶机ip arp-scan -l 2.探测目标靶机开放端口和服务情况 nmap -A -sV -p- 192.168.…

SpringCloud之微服务环境搭建

目录 1、微服务介绍 1.1.微服务架构介绍 1.2.微服务架构的常见问题 1.3.微服务架构的常见解决方案 1.3.1.ServiceComb ​ 1.3.2.SpringCloud 1.3.3.SpringCloud Alibaba 2、微服务环境搭建 2.1案例准备 技术选项&#xff1a; 模块设计&#xff1a; …

pyqt5打包后的exe文件在网吧windoes7系统运行报错问题

方案一 用录屏软件录个视频&#xff0c;然后用播放软件打开逐帧查看&#xff0c;找到报错原因&#xff0c;一般是某个包导入错误&#xff0c;重新安装下对应的包。(忒麻烦) 方案二 生成的exe文件你们执行的话&#xff0c;cmd黑框肯定会一闪而过&#xff0c;但是没有关系&…

[附源码]Nodejs计算机毕业设计基于Java的在线点餐系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

使用 VADER 对股票新闻进行情绪分析

什么是情感分析&#xff1f; 提示&#xff1a;情感分析 定义&#xff1a; 情感分析或意见挖掘是自然语言处理 (NLP)的一个子领域&#xff0c;它试图在给定文本中识别和提取意见。情感分析的目的是根据文本中主观性的计算处理来衡量说话者/作者的态度、情绪、评价、态度和情绪…

WIN10 共享文件夹并取消密码访问

目录 一、前言 二、共享文件过程 1、选择需要共享的文件夹右键-授予访问权限-特定用户 2、选择共享用户Everyone并点击添加 3、再点击右下角共享&#xff0c;一个共享目录就生成了 4、但是别人访问还需要提供密码&#xff0c;非常不方便&#xff0c;此时需要关闭密码 一、…

DataHub Docker安装 PostreSQL元数据集成

install docker & docker-compose&#xff0c;包括docker-compose升级 curl -L https://github.com/docker/compose/releases/download/v2.14.0/docker-compose-uname -s-uname -m-o /usr/local/bin/docker-compose install jq wget http://dl.fedoraproject.org/pub/epel…

前端大文件上传及切片上传-提升上传效率

一、使用场景&#xff1a; 1.大文件上传 2.网络环境环境不好&#xff0c;存在需要重传风险的场景 二、名词解释&#xff1a; 切片上传&#xff1a;也叫分片上传&#xff0c;就是将所要上传的文件&#xff0c;按照一定的大小&#xff0c;将整个文件分隔成多个数据块来进行分…

【Docker】Docker如何构建自己的镜像?从镜像构建到推送远程镜像仓库图文教程

专栏往期文章 《Docker是什么&#xff1f;Docker从介绍到Linux安装图文详细教程》《30条Docker常用命令图文举例总结》 本期目录专栏往期文章1. 构建镜像2. 本地镜像发布到公有云3. 本地镜像发布到私有云1. 构建镜像 提交构建镜像的命令如下&#xff1a; $ docker commit -m…

大二学生《web课程设计》中华英雄人物介绍袁隆平HTML+CSS+JavaScript(期末考核大作业)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

2021年网络安全省赛--服务器内部信息获取解析(中职组)

2021年省赛服务器内部信息获取解析 任务环境说明:Linux20210510 服务器场景操作系统:未知 (关闭连接) 服务器场景操作系统:Linux(封闭靶机) 用户名:test密码:123456 1.收集服务器场景中的服务信息。并获取服务器中开放的端口号信息,将服务器端口号作为flag提交…

GCN解读并附数据处理代码

此文GCN不是之前提到的lightGCN&#xff0c;而是真正的GCN图卷积&#xff0c;这个问题源于paper分类&#xff0c;同样是GAT所用的数据&#xff0c;其中paper之前的引用关系构成了图的边信息&#xff0c;之所以称之为半监督,并不是因为部分paper没有label及embedding信息&#x…

Nacos--命名空间、分组、ID的概念及用法

原文网址&#xff1a;Nacos--命名空间、分组、ID的概念及用法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Nacos的命名空间、分组、ID的概念及用法。 Nacos通过命名空间&#xff08;Namespace&#xff09;分组&#xff08;Group&#xff09;应用&#xff08;Data ID或Name&#…