SpringBoot+VUE实现文件导入并将其保存到Liunx系统

简介: 需求: 必须支持PDF、docx、xlsx、xls、doc等格式, 文件上传后保存在本地文件夹, 需要进行在线预览

一、需求

  • 必须支持PDF、docx、xlsx、xls、doc等格式
  • 文件上传后保存在本地文件夹
  • 需要进行在线预览

# 二、前端代码实现

## 2.1 显示实现
首先我们需要添加一个用于操作的按钮上去,像这样的:

<a @click="fileRef.onOpen(record)">查看文件</a>
<a @click="ProfessionImpExpRef.onOpen(record)">上传文件</a>

2.1.1 a标签实现

2.1.1.1 上传标签实现

当用户点击上传文件时我们会在右侧打开一个抽屉,用以展示上传界面,页面部分像这样:

<ProfessionImpExp ref="ProfessionImpExpRef" />

具体逻辑像这样:

//professionImpExp.vue 为上传界面
import ProfessionImpExp from './professionImpExp.vue'
const ProfessionImpExpRef = ref()

2.1.1.2 查看标签实现

文件查看部分基本和上传部分实现类似,页面部分:

<File ref="fileRef" @successful1="table.refresh(true)" />
//file.vue为文件查看界面
import File from "./file.vue";
const fileRef = ref()
const file = ref(null)

## 2.2 上传文件和文件查看界面实现
### 2.2.1 上传文件界面

2.2.1.1 上传文件界面展示部分

当用户点击上传文件时,我们需要打开一个界面用以提示用户支持的类型和上传注意事项、上传结果,像这样的:

<template>
    <xn-form-container title="导入导出" :width="700" :visible="visible" :destroy-on-close="true" @close="onClose">
        <span
            >导入数据格式严格按照要求进行数据录入,<b style="color: red">重复导入或使用重名文件将会覆盖之前数据内容</b>
        </span>
        <a-divider dashed />
        <div>
            <a-spin :spinning="impUploadLoading">
                <a-upload-dragger :show-upload-list="false" :custom-request="customRequestLocal" :accept="uploadAccept">
                    <p class="ant-upload-drag-icon">
                        <inbox-outlined></inbox-outlined>
                    </p>
                    <p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
                    <p class="ant-upload-hint">仅支持PDF、docx、xlsx、xls、doc格式文件</p>
                </a-upload-dragger>
            </a-spin>
        </div>
        <a-alert v-if="impAlertStatus" type="info" :show-icon="false" banner closable @close="onImpClose" class="mt-3">
            <template #description>
                <p>导入完成</p>
            </template>
        </a-alert>
    </xn-form-container>
</template>

展示出来就是下面的效果:
1.png

2.2.1.1 上传文件界面逻辑部分

用户点击后,我们会打开一个抽屉,并将获取到的本行数据id传入到这个界面和上传的数据文件进行绑定,之后传回后端处理。

import { message } from 'ant-design-vue'
    import professionApi from '@/api/biz/professionApi'
    import {cloneDeep} from "lodash-es";
    const impUploadLoading = ref(false)
    const impAlertStatus = ref(false)
    const dataId = ref()
    const impAccept = [
        {
            extension: '.xls',
            mimeType: 'application/vnd.ms-excel'
        },
        {
            extension: '.xlsx',
            mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
        },
        {
            extension: '.PDF',
            mimeType: 'application/pdf'
        },
        {
            extension: '.docx',
            mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        },
        {
            extension: '.doc',
            mimeType: 'application/msword'
        },
    ]
    // 指定能选择的文件类型
    const uploadAccept = String(
        impAccept.map((item) => {
            return item.mimeType
        })
    )
    // 导入
    const customRequestLocal = (data) => {
        impUploadLoading.value = true
        // 校验上传文件扩展名和文件类型是否为支持类型
        const extension = '.'.concat(data.file.name.split('.').slice(-1).toString().toLowerCase())
        const mimeType = data.file.type
        // 提取允许的扩展名
        const extensionArr = impAccept.map((item) => item.extension)
        // 提取允许的MIMEType
        const mimeTypeArr = impAccept.map((item) => item.mimeType)
        if (!extensionArr.includes(extension) || !mimeTypeArr.includes(mimeType)) {
            message.warning('上传文件类型仅支持PDF、word、excel、xls、xlsx格式文件!')
            impUploadLoading.value = false
            return false
        }

        const formData = new FormData();
        formData.append("file",data.file)
        formData.append("id",dataId.value.id)
        return professionApi
            .professionImport(formData)
            .then((res) => {
                impAlertStatus.value = res
            })
            .finally(() => {
                impUploadLoading.value = false
            })
    }
    // 关闭导入提示
    const onImpClose = () => {
        impAlertStatus.value = false
    }
    // 定义emit事件
    const emit = defineEmits({ successful: null })
    // 默认是关闭状态
    let visible = ref(false)
    const submitLoading = ref(false)

    // 打开抽屉
    const onOpen = (record) => {
        visible.value = true
        if (record) {
            let recordData = cloneDeep(record)
            dataId.value = Object.assign({}, recordData)
        }
    }
    // 关闭抽屉
    const onClose = () => {
        visible.value = false
        // 关闭导入的提示
        onImpClose()
    }

    // 调用这个函数将子组件的一些数据和方法暴露出去
    defineExpose({
        onOpen
    })

### 2.2.2 查看文件界面
#### 2.2.2.1 查看文件界面展示部分
用户点击查看文件后,进入到这个界面进行文件查看和在线展示。

<template>
    <xn-form-container
        :title="'文件详情'"
        :width="500"
        :visible="visible"
        :destroy-on-close="true"
        @close="onClose"
    >
        <a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
            <a-form-item label="文件:" name="url">
                <ol>
                    <li v-for="data in formData">
                        <a :href="'http://view.officeapps.live.com/op/view.aspx?src=https://img.qcybj.com/file/'+data" target="_blank"> {{ data }}</a>
                        
                    </li>
                </ol>
            </a-form-item>
        </a-form>
        <template #footer>
            <a-button style="margin-right: 8px" @click="onClose">关闭</a-button>
        </template>
    </xn-form-container>
</template>

具体的效果类似这种:
2.png

#### 2.2.2.2 查看文件界面逻辑部分
这部分的逻辑很简单,主要就是对后端的数据解析并绑定。

import { cloneDeep } from 'lodash-es'
import XnFormContainer from "@/components/XnFormContainer/index.vue";

// 抽屉状态
const visible = ref(false)
const emit = defineEmits({ successful: null })
const file = ref()
const submitLoading = ref(false)
const formData = ref({})
// 打开抽屉
const onOpen = (record) => {
    visible.value = true
    if (record) {
        let recordData = cloneDeep(record)
        let str = Object.assign({}, recordData).url.substr(1)
        let str1 = str.substring(0,str.length-1)
        formData.value = str1.split(",")
    }
}
// 关闭抽屉
const onClose = () => {
    visible.value = false
}
// 默认要校验的
const formRules = {
}
// 抛出函数
defineExpose({
    onOpen
})

## 2.2 其他逻辑

2.2.1 本行数据id选择逻辑

const selectedRowKeys = ref([])
    // 列表选择配置
    const options = {
        // columns数字类型字段加入 needTotal: true 可以勾选自动算账
        alert: {
            show: true,
            clear: () => {
                selectedRowKeys.value = ref([])
            }
        },
        rowSelection: {
            onChange: (selectedRowKey, selectedRows) => {
                selectedRowKeys.value = selectedRowKey
            }
        }
    }

2.2.2 API 逻辑

这部分内容可以参照我之前的文章,这里不再多说~

    //业务导入
    professionImport(data) {
        return request('import', data)
    },

三、后端实现

后端部分大多分逻辑SpringBoot + Ant Design Vue实现数据导出功能都有提及,这里不再多言。我们之说最重要的实现逻辑:

  @Transactional(rollbackFor = Exception.class)
    @Override
    public String importProfession(MultipartFile file,String id ) throws IOException {
        Profession profession = baseMapper.selectById(id);
        String urlList = profession.getUrl();
        //定义文件名
        String imgName = file.getOriginalFilename();
        //定义上传路径
        String upPath = path + imgName;
        byte[] bytes = new byte[1024];
        int dataLine;
        try(BufferedInputStream bufferedInputStream = new BufferedInputStream(file.getInputStream());
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(upPath))){
            while ((dataLine=bufferedInputStream.read(bytes))!= -1){
                bufferedOutputStream.write(bytes, 0, dataLine);
            }
        }
        if(urlList == null){
            profession.setUrl(imgName);
        }else {
            List<String> list = Arrays.asList(urlList.split(","));
            List<String> arrList = new ArrayList<>(list);
            arrList.add(imgName);
            profession.setUrl(arrList.toString());
        }
        QueryWrapper<Profession> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Profession::getId,id);
        this.update(profession,queryWrapper);
        return imgName;
    }

这里有一点需要注意,Java的IO方法很多,需要注意的是liunx系统中写入要指定绝对路径,不要用相对路径。

比如 MultipartFile 的 transferTo() 方法就可能使用相对路径。一旦使用了相对路径你就会发现原本指定写入的路径前多出了它:

/tmp/tomcat.8080.450079304707479782/work/Tomcat/localhost/ROOT
目录
相关文章
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
128 62
|
20天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
101 13
|
28天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
2月前
|
Java 应用服务中间件
SpringBoot获取项目文件的绝对路径和相对路径
SpringBoot获取项目文件的绝对路径和相对路径
117 1
SpringBoot获取项目文件的绝对路径和相对路径
|
2月前
|
网络协议 Java
springboot配置hosts文件
springboot配置hosts文件
52 11
|
2月前
|
存储 前端开发 JavaScript
|
2月前
|
XML Java 数据库连接
SpringBoot集成Flowable:打造强大的工作流管理系统
在企业级应用开发中,工作流管理是一个核心组件,它能够帮助我们定义、执行和管理业务流程。Flowable是一个开源的工作流和业务流程管理(BPM)平台,它提供了强大的工作流引擎和建模工具。结合SpringBoot,我们可以快速构建一个高效、灵活的工作流管理系统。本文将探讨如何将Flowable集成到SpringBoot应用中,并展示其强大的功能。
326 1
|
2月前
|
存储 Java API
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
121 2
|
2月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。