当前位置: 首页 > news >正文

在SSM+vue项目中上传表单数据和文件

从前端向后端发送multipart/form-data 类型数据(主要用于文件上传或表单提交)

如发送如下信息:

前端代码vue文件:(配置了服务器代理)

<template><div class="content"><el-form :model="form" ref="form" :rules="rules"  label-width="100px" class="demo-form"><div class="infoAdd"><div class="title">申请信息</div><el-form-item label="申请人" prop="proposer_Name"><el-input v-model="form.proposer_Name" placeholder="请输入申请人姓名"></el-input></el-form-item><el-form-item label="联系电话" prop="telephone"><el-input v-model="form.telephone" placeholder="请输入手机号码"></el-input></el-form-item><el-form-item label="身份证号" prop="cardId"><el-input v-model="form.cardId" placeholder="请输入身份证号"></el-input></el-form-item><el-form-item label="营业执照" prop="charterName"><el-input v-model="form.charterName" placeholder="请输入营业执照(名称)"></el-input></el-form-item><el-form-item label="所在区域" prop="location"><el-cascaderv-model="form.location":options="areaOptions":props="{ value: 'name',    // 绑定选项的value字段label: 'name',    // 绑定选项的label字段children: 'children', // 绑定子级字段expandTrigger: 'hover' }"clearable@change="handleAreaChange"placeholder="请选择所在区域"style="width: 100%;"></el-cascader></el-form-item><el-form-item label="详细地址" prop="address"><el-input v-model="form.address" placeholder="请输入详细地址"></el-input></el-form-item><el-form-item label="踏勘时间" prop="expDate"><el-date-pickerv-model="form.expDate"type="date"placeholder="请选择踏勘时间"value-format="yyyy-MM-dd"style="width: 100%;"></el-date-picker></el-form-item><el-form-item label="申请说明" prop="applyState"><el-inputtype="textarea"v-model="form.applyState"placeholder="请输入详细的申请说明":rows="4"></el-input></el-form-item></div><div class="infoAdd"><div class="title" style="margin-top: 20px;">已上传资料</div><div class="items"><!-- 上传的图片 --><div class="itemAdd" v-for="(img, index) in uploadedImages" :key="'img-'+index"><img :src="img.preview" alt="上传图片"><div class="cancel" @click="removeUploadedItem('image', index)">×</div></div><!-- 上传的视频 --><div class="itemAdd" v-for="(video, index) in uploadedVideos" :key="'video-'+index"><video controls width="100" height="80"><source :src="video.preview" :type="video.type">您的浏览器不支持视频预览</video><div class="cancel" @click="removeUploadedItem('video', index)">×</div></div><!-- 上传的文件 --><div class="itemAdd file-item" v-for="(file, index) in uploadedDocs" :key="'file-'+index"><div class="file-icon"><i class="el-icon-document"></i></div><div class="file-name">{{ truncateFileName(file.name) }}</div><div class="cancel" @click="removeUploadedItem('doc', index)">×</div></div></div></div><div class="infoAdd"><div class="title">上传资料</div><div class="upload-container"><div class="upload-item" @click="triggerUpload('img')"><div class="upload-icon">+</div><div class="upload-text">图片</div><input type="file" ref="img" @change="handleImageUpload" hidden accept="image/*" multiple></div><div class="upload-item" @click="triggerUpload('video')"><div class="upload-icon">+</div><div class="upload-text">录像</div><input type="file" ref="video" @change="handleVideoUpload" hidden accept="video/*" multiple></div><div class="upload-item" @click="triggerUpload('file')"><div class="upload-icon">+</div><div class="upload-text">文件</div><input type="file" ref="file" @change="handleFileUpload" hidden multiple></div></div></div><div class="sumBtn"><el-button type="primary" @click="submitForm('form')" size="large">提交</el-button></div></el-form></div>
</template><script>
import chinaRegionData from '@/assets/data/china-region.json';
export default {  name: 'addInfo',data() {// 手机号校验函数const validatePhone = (rule, value, callback) => {if (!value) {return callback(new Error('请输入联系电话'));}if (!/^1[3-9]\d{9}$/.test(value)) {return callback(new Error('请输入正确的手机号码'));}callback();};// 身份证校验函数const validateIdCard = (rule, value, callback) => {if (!value) {return callback(new Error('请输入身份证号'));}// 简单校验18位(实际项目应使用更严格校验)if (!/(^\d{15}$)|(^\d{17}(\d|X|x)$)/.test(value)) {return callback(new Error('请输入有效的身份证号'));}callback();};return {form: {proposer_Name: '',telephone: '',cardId: '',charterName: '',location: [],address: '',expDate: '',applyState: ''},areaOptions: chinaRegionData,uploadedImages: [], // 上传的图片uploadedVideos: [], // 上传的视频uploadedDocs: []  , // 上传的文件rules: {proposer_Name: [{ required: true, message: '请输入申请人姓名', trigger: 'blur' },{ min: 2, max: 10, message: '长度在2到10个字符', trigger: 'blur' }],telephone: [{ required: true, validator: validatePhone, trigger: 'blur' }],cardId: [{ required: true, validator: validateIdCard, trigger: 'blur' }],charterName: [{ required: true, message: '请输入营业执照名称', trigger: 'blur' }],location: [{ type: 'array', required: true, message: '请选择所在区域', trigger: 'change' }],address: [{ required: true, message: '请输入详细地址', trigger: 'blur' },{ min: 5, max: 100, message: '长度在5到100个字符', trigger: 'blur' }],expDate: [{ required: true, message: '请选择踏勘时间', trigger: 'change' }],applyState: [{ required: true, message: '请输入申请说明', trigger: 'blur' },{ min: 10, message: '至少输入10个字符', trigger: 'blur' }]},}},methods: {handleAreaChange(value) {console.log('Area changed:', value);},triggerUpload(type) {this.$refs[type].click();},handleImageUpload(event) {const files = event.target.files;if (!files || files.length === 0) return;Array.from(files).forEach(file => {if (!file.type.match('image.*')) {this.$message.error(`${file.name} 不是有效的图片文件`);return;}const reader = new FileReader();reader.onload = (e) => {this.uploadedImages.push({file: file,preview: e.target.result,type: file.type});};reader.readAsDataURL(file);});console.log("uploadedImages:"+this.uploadedImages)},handleVideoUpload(event) {const files = event.target.files;if (!files || files.length === 0) return;Array.from(files).forEach(file => {if (!file.type.match('video.*')) {this.$message.error(`${file.name} 不是有效的视频文件`);return;}const reader = new FileReader();reader.onload = (e) => {this.uploadedVideos.push({file: file,preview: e.target.result,type: file.type});};reader.readAsDataURL(file);});console.log("uploadedVideos:"+this.uploadedVideos)},handleFileUpload(event) {const files = event.target.files;if (!files || files.length === 0) return;Array.from(files).forEach(file => {this.uploadedDocs.push({file: file,name: file.name,type: file.type,size: file.size});});this.$message.success(`已添加 ${files.length} 个文件`);console.log("uploadedDocs:"+this.uploadedDocs)},removeUploadedItem(type, index) {if (type === 'image') {this.uploadedImages.splice(index, 1);} else if (type === 'video') {this.uploadedVideos.splice(index, 1);} else {this.uploadedDocs.splice(index, 1);}this.$message.success('已删除');},truncateFileName(name) {if (name.length > 10) {return name.substring(0, 8) + '...';}return name;},submitForm(formName) {this.$refs[formName].validate((valid)=>{if (!valid) {this.$message.error('请检查表单填写是否正确');return false;}// 检查是否上传了文件(如果需要)if (this.uploadedImages.length === 0 && this.uploadedVideos.length === 0 && this.uploadedDocs.length === 0) {this.$message.warning('请至少上传一份资料');return;}console.log("from"+this.form)// Prepare form dataconst formData = new FormData();formData.append('proposer_Name', this.form.proposer_Name);formData.append('telephone', this.form.telephone);formData.append('cardId', this.form.cardId);formData.append('charterName', this.form.charterName);formData.append('location', this.form.location.join('/'));formData.append('address', this.form.address);formData.append('expDate', this.form.expDate);formData.append('applyState', this.form.applyState);formData.append('licenseName',this.$route.query.name)// Add filesthis.uploadedImages.forEach(item => formData.append('img', item.file));this.uploadedVideos.forEach(item => formData.append('video', item.file));this.uploadedDocs.forEach(item => formData.append('file', item.file));console.log("formData:"+this.formData)console.log("uploadedImages:"+this.uploadedImages)console.log("uploadedVideos:"+this.uploadedVideos)console.log("uploadedDocs:"+this.uploadedDocs)// Submit formthis.$axios.post('/api/user/addUser', formData).then(response => {console.log("response.data"+response.data)if(response.data==='success'){this.$message.success('提交成功');this.$router.push({path:'/infoList'})}}).catch(error => {this.$message.error('提交失败');console.error(error);});})}}
}
</script><style scoped>
.content {height: 100%;width: 100%;padding: 20px;box-sizing: border-box;overflow-y: auto;
}.infoAdd {width: 95%;margin: 0 auto;margin-top: 20px;border: 1px solid #EBEEF5;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);padding: 20px;border-radius: 4px;background-color: #fff;
}.title::before {content: '';display: inline-block;width: 4px;height: 14px;background-color: #409EFF;margin-right: 8px;vertical-align: middle;
}.title {font-size: 16px;margin-bottom: 20px;font-weight: bold;color: #303133;
}.items {margin-top: 12px;display: flex;justify-content: flex-start;flex-wrap: wrap;gap: 10px;
}.itemAdd {width: 100px;height: 80px;background-color: #F5F7FA;border: 1px dashed #DCDFE6;border-radius: 4px;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;transition: all 0.3s;position: relative;
}.itemAdd:hover {border-color: #409EFF;
}.itemAdd img {width: 100%;height: 100%;object-fit: cover;border-radius: 4px;
}.upload-container {display: flex;justify-content: space-between;margin-top: 15px;
}.upload-item {flex: 1;margin: 0 5px;height: 100px;background-color: #F5F7FA;border: 1px dashed #DCDFE6;border-radius: 4px;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;transition: all 0.3s;
}.upload-item:hover {border-color: #409EFF;
}.upload-icon {font-size: 24px;width: 40px;height: 40px;color: #909399;border: 1px solid #DCDFE6;box-sizing: border-box;border-radius: 50%;display: flex;justify-content: center;align-items: center;margin-bottom: 8px;
}.upload-text {font-size: 14px;color: #606266;
}.cancel {position: absolute;font-size: 16px;right: -8px;top: -8px;width: 20px;height: 20px;background: #f56c6c;color: white;border-radius: 50%;display: flex;align-items: center;justify-content: center;cursor: pointer;z-index: 2;
}.file-item {display: flex;flex-direction: column;align-items: center;justify-content: center;
}.file-icon {margin-bottom: 5px;
}.file-icon i {font-size: 30px;color: #409EFF;
}.file-name {font-size: 12px;text-align: center;width: 90px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}video {background-color: #000;border-radius: 4px;
}.sumBtn {width: 95%;margin: 30px auto;text-align: center;
}.example {color: #909399;font-size: 14px;
}.el-form-item {margin-bottom: 20px;
}.el-cascader {width: 100%;
}.el-date-editor {width: 100%;
}.el-textarea__inner {min-height: 80px !important;
}
</style>

以下是基于SSM框架的后端实现代码,用于接收前端发送的表单数据和文件,并保存到数据库中的步骤:(在成功创建SSM项目并能启动成功之后)

1.在 pom.xml 中添加文件上传支持库:

<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.4</version>
</dependency>
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version>
</dependency>

2.在 spring-mvc.xml 中配置 MultipartResolver:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!-- 设置编码 --><property name="defaultEncoding" value="UTF-8"/><!-- 最大上传大小(单位:字节) --><property name="maxUploadSize" value="10485760"/> <!-- 10MB --><!-- 内存缓冲大小 --><property name="maxInMemorySize" value="40960"/>
</bean>

还可能需要解决跨域问题:

<!--    配置CORS(跨域资源共享)的设定。它的作用是允许web应用处理来自不同源的跨域请求--><mvc:cors><mvc:mapping path="/**"allowed-origins="*"allowed-methods="GET, POST, PUT, DELETE, OPTIONS"allowed-headers="Content-Type"/></mvc:cors>

3.创建实体类,接收表单数据

@Data
public class User {private int id;private String proposerName;private String licenseName;private String telephone;private String cardId;private String charterName;private String location;private String address;private Date expDate;private String applyState;
}

4.mapper接口

public interface UserMapper {//添加申请人void addUser(User user);//更新申请人void updateUser(@Param("id") Integer id,@Param("type") String type,@Param("filePath") String fliePath);
}

5.mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yuankan.mapper.UserMapper"><resultMap id="userMap" type="com.yuankan.pojo.User"><!-- 主键字段使用id标签 --><id property="id" column="id" /><!-- 普通字段使用result标签 --><result property="proposerName" column="proposer_Name" /><result property="licenseName" column="licenseName"/><result property="telephone" column="telephone" /><result property="cardId" column="cardId" /><result property="charterName" column="charterName" /><result property="location" column="location" /><result property="address" column="address" /><result property="expDate" column="expDate" /><result property="applyState" column="applyState" /></resultMap><insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">insert into user  values(null,#{proposerName},#{licenseName},#{telephone},#{cardId},#{charterName},#{location},#{address},#{expDate}, #{applyState},null,null,null)</insert><update id="updateUser">update user<set><if test="type=='image'">img = #{filePath},</if><if test="type=='video'">video = #{filePath},</if><if test="type=='file'">file = #{filePath},</if></set>WHERE id = #{id}</update>
</mapper>

6.service接口

public interface UserService {//添加申请人void addUser(User user, List<MultipartFile> images,List<MultipartFile> videos,List<MultipartFile> files);}

7.service接口实现

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;/*** 添加申请人* @param user*/@Overridepublic void addUser(User user, List<MultipartFile> images,List<MultipartFile> videos,List<MultipartFile> files) {//保存申请人信息到数据库userMapper.addUser(user);// 保存上传的文件try {saveUploadedFiles(images, "image", user.getId());saveUploadedFiles(videos, "video", user.getId());saveUploadedFiles(files, "file", user.getId());} catch (IOException e) {throw new RuntimeException("文件保存失败", e);}}/*** 保存上传的文件* @param files 上传的文件* @param type 文件类型* @param id 申请人id* @throws IOException*/private void saveUploadedFiles(List<MultipartFile> files, String type, Integer id) throws IOException {// 如果没有上传文件,则返回if (files == null || files.isEmpty()) return;// 遍历文件列表,保存文件到服务器for (MultipartFile file : files) {String filePath = FileUploadUtil.saveFile(file, type);// 更新数据库中的文件路径userMapper.updateUser(id,type,filePath);}}}

FileUploadUtil工具类:

/*** 文件上传工具类*/
public class FileUploadUtil {// 上传目录private static final String UPLOAD_DIR = "/uploads/";/*** 保存上传的文件* @param file 上传的文件* @param type 文件类型* @return* @throws IOException*/public static String saveFile(MultipartFile file, String type) throws IOException {// 创建上传目录(如果不存在)File uploadDir = new File(UPLOAD_DIR + type);if (!uploadDir.exists()) {// 创建目录uploadDir.mkdirs();}// 生成唯一文件名String fileName = file.getOriginalFilename();//文件路径Path filePath = Paths.get(uploadDir.getAbsolutePath(), fileName);// 保存文件Files.copy(file.getInputStream(), filePath);// 返回文件路径return filePath.toString();}
}

8.Controller层(使用日志记录信息)

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;//获取Logger对象private static  final Logger logger= LogManager.getLogger(UserController.class);@PostMapping("/addUser")public String addUser(@RequestParam String proposer_Name,@RequestParam String telephone,@RequestParam String cardId,@RequestParam String charterName,@RequestParam String[] location, // 地区数组@RequestParam String address,@RequestParam Date expDate,@RequestParam String applyState,@RequestParam String licenseName,@RequestParam(required = false) MultipartFile[] img,@RequestParam(required = false) MultipartFile[] video,@RequestParam(required = false) MultipartFile[] file){// 创建申请人对象User user = new User();user.setProposerName(proposer_Name);logger.info("申请人姓名:"+proposer_Name);user.setTelephone(telephone);logger.info("申请人电话:"+telephone);user.setCardId(cardId);logger.info("申请人身份证号:"+cardId);user.setCharterName(charterName);logger.info("营业执照名称:"+charterName);user.setLocation(String.join("/", location));// 将数组转为字符串logger.info("申请人地址:"+String.join("/", location));user.setAddress(address);logger.info("申请人详细地址:"+address);user.setExpDate(expDate);logger.info("申请人有效期:"+expDate);user.setApplyState(applyState);logger.info("申请说明:"+applyState);user.setLicenseName(licenseName);logger.info("许可证名称:"+licenseName);logger.info("user:"+user);// 处理上传文件List<MultipartFile> images = img != null ? Arrays.asList(img) : null;List<MultipartFile> videos = video != null ? Arrays.asList(video) : null;List<MultipartFile> files = file != null ? Arrays.asList(file) : null;// 添加申请人userService.addUser(user, images, videos, files);return "success";}
}

日志使用:

1.导入依赖:

<!-- Log4j2 --><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-core</artifactId><version>2.17.2</version></dependency><dependency><groupId>org.apache.logging.log4j</groupId><artifactId>log4j-slf4j-impl</artifactId><version>2.17.2</version></dependency>

2.编写log4j2.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"><Appenders><Console name="Console" target="SYSTEM_OUT"><PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/></Console><!--        <RollingFile name="RollingFile" fileName="logs/ssm.log"-->
<!--                     filePattern="logs/ssm-%d{yyyy-MM-dd}.log">-->
<!--            <PatternLayout>-->
<!--                <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n</Pattern>-->
<!--            </PatternLayout>-->
<!--            <Policies>-->
<!--                <TimeBasedTriggeringPolicy interval="1" modulate="true"/>-->
<!--            </Policies>-->
<!--            <DefaultRolloverStrategy max="30"/>-->
<!--        </RollingFile>--></Appenders><Loggers><Root level="info"><AppenderRef ref="Console"/>
<!--            <AppenderRef ref="RollingFile"/>--></Root><Logger name="com.it.dao" level="debug"/></Loggers>
</Configuration>

http://www.dtcms.com/a/267310.html

相关文章:

  • android开发中的 AndroidX 版本的查看 及 constraintLayout的简单用法
  • 【性能优化】程序性能优化:疏通胜于堵塞
  • 【Elasticsearch】检索高亮
  • 成为git砖家(12): 看懂git合并分支时冲突提示符
  • HTML初学者第三天
  • hono框架绑定cloudflare的d1数据库操作步骤
  • Redis基础的介绍与使用(一)(Redis简介以及Redis下载和安装)
  • Git 版本控制完全指南:从入门到精通
  • 【Halcon】WPF 自定义Halcon显示控件完整流程与 `OnApplyTemplate` 未触发的根本原因解析!
  • Web3 Study Log 003
  • 蓝牙墨水屏上位机学习(3)
  • Java 与 Vue 全栈开发:“一课一得“ 学习笔记系统实战
  • OneCode图表配置速查手册
  • CMake是什么
  • NV183NV185美光固态闪存NV196NV201
  • 供应链管理-采购管理:国际贸易及支付领域中常见的支持方式
  • FLUX.1-Kontext 高效训练 LoRA:释放大语言模型定制化潜能的完整指南
  • 软件版本FCCU(故障采集与控制单元)设计
  • 如何选择不会降低网站速度的WordPress主题
  • 动手实践OpenHands系列学习笔记11:现代开发流程
  • C#指针:解锁内存操作的底层密码
  • DVWA靶场通关笔记-验证码绕过reCAPTCHA(Medium级别)
  • 网安系列【6】之[特殊字符] SQL注入揭秘:从入门到防御实战指南
  • cloudflare配合github搭建免费开源影视LibreTV一个独享视频网站 详细教程
  • React Native 亲切的组件们(函数式组件/class组件)和陌生的样式
  • 百度开源文心一言4.5:论文解读和使用入门
  • 闲庭信步使用SV搭建图像测试平台:第三十二课——系列结篇语
  • 【学习笔记】MySQL技术内幕InnoDB存储引擎——第5章 索引与算法
  • MySQL(118)如何使用SSL进行加密连接?
  • 前端进阶之路-从传统前端到VUE-JS(第三期-VUE-JS配套UI组件的选择)(Element Plus的构建)