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

2023-11-03

一、需求

  • 必须支持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>

展示出来就是下面的效果:
在这里插入图片描述

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.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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • C#处理JSON格式数据

    欢迎您成为我的读者 希望这篇文章能给你一些帮助 前言 大家好 我是阿辉 这几年在编程行业要是说哪个方向的语言发展迅速 那必须是属于前端 前端新的框架层出不穷 写法也很多 但是有一种是不变的 就是当需要和后端进行数据传输交换的时候 一直在使用
  • JavaScript 跳格子游戏

    跳格子游戏棋盘 从起始点需要跳 n 个格子才能到结束点 每次可以跳 1 或 2 个格子 问共有多少种方法可以跳到结束点 输入 2 输出 2 解释 有两种方法可以爬到楼顶 1 1 格 1格 2 2 格 输入2 输出2 样例输入 3 样例输出
  • Java Nio(四)Java Nio实现HTTP请求

    HTTP相比于HTTPS来说要简单的多 完整代码在github上 https github com cxsummer net nio 我先说原理 在文章开始我先抛出一个问题 HTTP的GET和POS请求方法区别在哪呢 答案是除了名字没区别
  • 线程,任务,线程池的学习总结

    同步操作 synchornous operation 先完成其工作再返回调用者 异步操作 asynchornous operation 大部分工作是在返回给调用者之后才完成的 但是线程异步必须对资源进行协调 否则两个或者更多线程可能在同一时
  • vue 插件开发

    一见如故 vue插件就是一个install方法 或者有install方法的对象 在组件实例化或者挂载之前use 就行了 install方法的第一个参数就是app或者Vue 所以可以加一些全局的东西 作用 加全局组件 指令 全局方法等 示例代
  • 关于 Python 之 Matplotlib 的总结

    文章目录 通用 简单例子 中文显示问题 参数 颜色 color参数表 线型 linestyle ls 参数表 标记符号 marker参数表 位置 legend loc参数表 plt 常用命令 图形模板 柱形图 饼图 散点图 直方图 箱线图
  • 1、微信小程序-环境搭建、基本语法

    文章目录 前言 一 微信小程序简介 1 微信 程序 2 运行环境 3 环境准备 二 项目的目录结构 1 小程序项目结构与传统web相比 2 小程序基本目录结构 2 1 json 配置文件 2 1 模板语法 代码见demo01 2 2 wxs
  • 进程间通讯几种方式

    进程通信的目的 数据传输 一个进程需要将它的数据发送给另一个进程 发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据 一个进程对共享数据 通知事 一个进程需要向另一个或一组进程发送消息 通知它 它们 发生了某种事件 如
  • (2)minikube玩转k8s集群之安装kubectl和minikube

    配套视频教程 1 Minikube介绍 简单说 创建k8s集群很麻烦 minikube可以让我们快速搭建一个k8s集群用于学习 Minikube 是一种可以让您在本地轻松运行 Kubernetes 的工具 Minikube 在笔记本电脑上的
  • 从文本、图像到音视频,AIGC技术将如何重构我们的数字世界?

    引言 1950 年 艾伦 图灵提出著名的 图灵测试 给出判定机器是否具有智能的试验方法 16 年后 世界上第一款可人机对话的机器人 Eliza 问世 这是 AI 技术最早期的萌芽阶段 但由于当时的科技水平限制 AIGC 仅限于小范围实验 A
  • Git clone下载文件和依赖项 in cmd

    从github复制好你要下载的文件地址和依赖项 git clone https github com kibamin FuzzyConstraintClustering git pip install r requirements txt
  • LeetCode 56. 合并区间

    题目链接 点击这里 题意 给出一个区间的集合 请合并所有重叠的区间 思路 AC代码 class Solution public vector
  • 高德地图开发(二、地图控件)

    高德地图开发 二 地图插件加载 一 加载单个插件 二 加载多个插件 有同步 异步等方法 我用得都是异步 图层类型切换 MapType 与3D罗盘控制 AMap ControlBar 会重叠 需要另写样式让它们分开 插件类型链接 https
  • TensorFlow bug(六)——Dimensions of inputs should match: shape[0] = [1,600,750,3] vs. shape[2] = [1,600

    问题描述 在用R FCN目标检测API训练我自己的数据时 我对原代码做了些改动 原来的rfcn resnet101 coco config文件中 batch size的值为1即一次处理1张图片 我改成了别的数 2 8 16 32 都会出现如
  • Unity 3D物体、UI同屏||3D物体在UI界面显示

    先来猜一下这两个石头人 哪一个是UI 哪一个是3d物体 答案是中间那一个 这种UI显示3D物体适用很多场景 比如说商店展示模型展示 例子特效 2 5d游戏 实现起来很简单 接下来我们实现一下 实现步骤 1 新建一个相机 对准角色 2 UI新
  • usb设备拓扑关系

    usb设备拓扑关系 1 简介 2 usbfs和debugfs拓扑 2 1挂载方式 2 2拓扑关系信息说明 2 2 1usb总线上的设备信息 2 2 2总线上特定设备的详细信息 2 3debugfs中的设备拓扑信息 T Topology B
  • react之旅(八)React生命周期

    一 react 16前的生命周期 1 1 constructor constructor props context 如果想 在组件其他地方是可以直接接收的 使用props或context 则需要以参数形式传入 只要组件存在construc
  • 求n的阶乘的算法框图_你不知道的阶乘与gamma函数

    你不知道的阶乘 阶乘对于有数学基础的人来说都不陌生 简单理解就是数的累乘 10的阶乘10 10 9 8 7 6 5 4 3 2 1 但是我们有没有思考过 如分数的阶乘是如何运算的 有没有方法估算一个数的阶乘 其实 1 2的阶乘等于 的平方根
  • 入门级题解:剑指 Offer 06. 从尾到头打印链表

    题目地址 https leetcode cn com problems cong wei dao tou da yin lian biao lcof 主要是 vector 容器的用法 之前学过 忘了 vector的详解 https blog
  • SpringBoot+VUE实现文件导入并将其保存到Liunx系统

    SpringBoot VUE实现文件导入 一 需求 二 前端代码实现 2 1 显示实现 2 1 1 a标签实现 2 1 1 1 上传标签实现 2 1 1 2 查看标签实现 2 2 上传文件和文件查看界面实现 2 2 1 上传文件界面 2 2