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

news2024/11/13 16:34:58

SpringBoot+VUE实现文件导入

  • 一、需求
  • 二、前端代码实现
    • 2.1 显示实现
      • 2.1.1 a标签实现
        • 2.1.1.1 上传标签实现
        • 2.1.1.2 查看标签实现
    • 2.2 上传文件和文件查看界面实现
      • 2.2.1 上传文件界面
        • 2.2.1.1 上传文件界面展示部分
        • 2.2.1.1 上传文件界面逻辑部分
      • 2.2.2 查看文件界面
        • 2.2.2.1 查看文件界面展示部分
        • 2.2.2.2 查看文件界面逻辑部分
    • 2.2 其他逻辑
      • 2.2.1 本行数据id选择逻辑
      • 2.2.2 API 逻辑
  • 三、后端实现

一、需求

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

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

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

相关文章

一步一步学OAK之二: RGB相机控制

今天我们来实现 RGB相机的控制程序&#xff0c;用来控制彩色相机的曝光、灵敏度、白平衡、亮度/色度降噪、 设备端裁剪、相机触发器等。 目录 Setup 1: 创建文件Setup 2: 安装依赖Setup 3: 导入需要的包Setup 4: 全局变量Setup 5: 定义clamp函数Setup 6: 创建pipelineSetup 7:…

Nginx重写跳转

目录 一、rewrite跳转场景 二、rewrite跳转实现 三、rewrite实际场景 四、rewrite正则表达式 五、rewrite命令语法格式 六、location分类 1、location 大致可以分为三类 2、正则匹配的常用表达式 七、location 优先级 八、rewrite和location比较 九、总结 一、rewri…

9 HAL库驱动框架简述(STM32HAL库)

目录 HAL库驱动框架简述 HAL库外设设计思想 HAL库和Cube MX相结合 一、对外设的封装——句柄结构体 二、外设初始化 初始化结构体 初始化的逻辑 三、外设使用逻辑 通用接口函数 初始化函数 I/O操作函数 控制函数 状态参数 扩展接口函数 总结 补充&#xff1a;H…

如何以最小成本通过CMMI评估?评估调查问卷收集中

CMMI评估&#xff0c;我们经常遇到&#xff1a;评估费用高、时间长&#xff0c;CMMI标准过程无法高效落地&#xff0c;那么我们如何以最小的成本通过CMMI评估&#xff1f; CoCode开发云公益直播课即将开播&#xff01;直播主题&#xff1a;如何以最小成本通过CMMI评估。为了更好…

怎么把文字生成图片?三款ai绘画生成器分享

如果你对ai绘画工具有一定了解的话&#xff0c;你就会知道&#xff1a;市面上大部分ai绘画工具都是收费。再退一步讲&#xff0c;我们暂且不论收费价格的高低&#xff0c;大多数收费的ai绘画工具也不一定能准确匹配我们的需求。 仅仅在学生党和工作党之间&#xff0c;对ai绘画…

python爬虫增加多线程获取数据

Python爬虫应用领域广泛&#xff0c;并且在数据爬取领域处于霸主位置&#xff0c;并且拥有很多性能好的框架&#xff0c;像Scrapy、Request、BeautifuSoap、urlib等框架可以实现爬行自如的功能&#xff0c;只要有能爬取的数据&#xff0c;Python爬虫均可实现。数据信息采集离不…

windows10教育版过期,记录一下重装windows11专业工作站。报错“若要在此计算机上安装windows,请重新启动安装”

准确的来说是重装 windows10或者windows11都有问题&#xff0c;而且卡了很久&#xff1b;最初的问题是 第一步解决问题的方法&#xff1a; 1、修改注册文件&#xff1a; 有些不显示鼠标&#xff0c;记住鼠标按住拖动&#xff0c;这样可以看见矩形的样子&#xff0c;可以知道大…

shardingsphere-proxy 搭建mysql的分库分表

1、docker安装mysql5.7版本 拉取mysql的镜像 docker pull mysql:5.7创建mysql的配置目录&#xff0c;日志目录&#xff0c;数据存储的目录 mkdir -p /home/sunyuhua/docker/mysql/conf mkdir -p /home/sunyuhua/docker/mysql/logs mkdir -p /home/sunyuhua/docker/mysql/dat…

服务器日志处理,文件截取关键字

临近年中述职&#xff0c;需要各种量化参数&#xff0c;服务稳定性是上半年的重中之重&#xff0c;所以需要重点列出说服性指标&#xff0c;因此各种错误吗的统计信息便是重要信息&#xff0c;因为公司的日志采集系统因上云缘故&#xff0c;导致历史数据丢失没法查询&#xff0…

Jetpack Compose实现 验证码输入框

highlight: androidstudio Jetpack Compose 作为 Android 的新一代 UI 开发框架,提供了非常强大的工具来构建用户界面。 今天,我们就利用 Compose 来实现一个炫酷的验证码输入框! 开始的思路是用6个TextField来实现 // 用于存储验证码的长度 val codeLength 6 // 定义一个…

Cesium 实战 - 使用 gltf-vscode 查看、预览以及编辑 glTF 和 GLB 模型

Cesium 实战 - 使用 gltf-vscode 查看、预览以及编辑 glTF 和 GLB 模型 VScode&#xff08;Visual Studio Code&#xff09; 安装模型必要插件VScode 预览自定义关节&#xff08;articulations&#xff09;动作VScode 导入 GLB 格式模型VScode 导出 GLB 格式模型 模型渲染作为 …

【什么是iMessage苹果推】怎样来获取设备令牌(Device Token)实现步骤

要获取设备令牌&#xff08;Device Token&#xff09;&#xff0c;您需要在应用程序中实现以下步骤&#xff1a; 在应用程序中请求用户授权&#xff1a;您需要请求用户授权允许应用程序发送远程通知。这可以通过使用 UNUserNotificationCenter&#xff08;User Notifications …

Linux学习之以openresty为例学习源码安装软件

https://github.com/openresty/openresty/tags里边有openresty各个版本的源码。 https://openresty.org/en/是官网。 wget https://github.com/openresty/openresty/archive/refs/tags/v1.15.8.1.tar.gz(github网址)或者wget https://openresty.org/download/openresty-1.15.…

6月29日第壹简报,星期四,农历五月十二

6月29日第壹简报&#xff0c;星期四&#xff0c;农历五月十二&#xff0c;早安&#xff01;坚持阅读&#xff0c;静待花开1. 中国移动元宇宙产业联盟成立&#xff0c;科大讯飞、华为、小米等为首批成员。2. 离岸人民币兑美元跌破7.25关口&#xff0c;创去年11月末来低位。3. 成…

STC89C52与LCD1602液晶显示的软硬件仿真

STC89C52与LCD1602液晶显示的软硬件仿真 硬件仿真平台&#xff1a;protues8.13 软件仿真平台&#xff1a;keil5 硬件连接图&#xff1a; 软件代码实现&#xff1a; &#xff08;复制后 粘贴到keil5中&#xff0c;即可使用&#xff0c;无需修改&#xff09; #include <RE…

SpringCloud-Nacos注册中心

文章目录 Nacos注册中心服务注册到nacos1&#xff09;引入依赖2&#xff09;配置nacos地址3&#xff09;重启 5.3.服务分级存储模型给user-service配置集群同集群优先的负载均衡 权重配置环境隔离创建namespace给微服务配置namespace Nacos与Eureka的区别 Nacos注册中心 服务注…

v8-tc39-ecma262: at,代替“arr[0]“取值

首先是语义化 其次是函数式&#xff0c;意味着加入流式调用队列 如上图&#xff0c;解释如下&#xff1a; 对象&#xff0c;调用对象函数处理类数组&#xff0c;调用类数组处理关联下标&#xff1f;转为Integer或者Infinity类型如果下标的值大于等于0&#xff0c;则设置赋值给…

Windows 驱动开发环境搭建

Windows 驱动开发环境搭建及 windbg 调试工具安装使用 引言了解 Windows 驱动开发环境下载 Windows 驱动开发环境根据需要下载安装对应版本的 Visual Studio下载安装对应的 WDK 工具包 编写第一个驱动代码总结参考资料 引言 对于 Windows 驱动开发&#xff0c;在微软官方的文档…

go定时任务crontab

在linux里可以通过crontab -e或者vi /etc/crontab编辑定时任务&#xff0c;区别在于后者只有root用户可以&#xff0c;还可以指定shell环境&#xff0c;不建议修改&#xff0c;修改前建议备份&#xff0c;前者任何用户都可以使用&#xff0c;两者修改后都不用修改自动重启。 1…

尚无忧宠物托运小程序app源码前景如何?

宠物托运市场调研分析 由于宠物托运在交通运输中并不是一个很大的类目&#xff0c;行业尚缺乏标准的流程规范与相关的监管机制&#xff0c;目前我国市面上常见的三方宠物托运公司多无正规手续&#xff0c;更有多数公司不具备相关运输资质。 如今&#xff0c;宠物经济不断崛起…