需求:
JavaWeb的购物车系统需要实现新增商品的功能。商品的展示图片需要与商品的基础信息一同上传至服务器,图片保存路径则随基础信息异同保存至数据库。
实现:
前端:
1. 选择相应的文件上传组件:
选择点击按钮手动上传的组件,源代码如下:
<template>
<el-upload
ref="uploadRef"
class="upload-demo"
action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
:auto-upload="false"
>
<template #trigger>
<el-button type="primary">select file</el-button>
</template>
<el-button class="ml-3" type="success" @click="submitUpload">
upload to server
</el-button>
<template #tip>
<div class="el-upload__tip">
jpg/png files with a size less than 500kb
</div>
</template>
</el-upload>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import type { UploadInstance } from 'element-plus'
const uploadRef = ref<UploadInstance>()
const submitUpload = () => {
uploadRef.value!.submit()
}
</script>
后端MultipartFile对应的是前端FormData数据,为成功上传至后端,需要对上传过程进行一些修改,将自定义的上传过程编写在before-upload周期中,返回值为false则不会调用默认的上传方法
<template>
<el-upload
ref="uploadRef"
class="upload-demo"
action="http://localhost:8090/img/upload"
:auto-upload="false"
:name="file"
:before-upload="handleBeforeUpload"
@change="handleChange"
:limit="1"
>
<template #trigger>
<el-button type="primary">select file</el-button>
</template>
<el-button class="ml-3" type="success" @click="submitUpload">
upload to server
</el-button>
</el-upload>
</template>
<script setup>
import { ref } from 'vue'
import axios from 'axios'
const uploadRef = ref()
const submitUpload = () => {
uploadRef.value.submit()
}
const file = ref()
const handleChange = (e) => {
file.value = e.file; // 获取到的文件对象
// ...
};
const handleBeforeUpload = async (file) => {
// 使用axios发送文件前,确保创建FormData实例
const formData = new FormData();
formData.append('file', file);
for (let pair of formData.entries()) {
console.log(pair[0] + ', ' + pair[1]);
}
// 配置axios请求
axios({
method: 'post',
url: 'http://localhost:8090/img/upload',
// 上传数据直接为formData实例即可,无需通过JSON封装,否则无法解析!!!
data: formData,
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
return false;
}
</script>
FormData类型的数据无法直接在控制台中log出来,直接log会生成空对象{},用遍历方式便可输出FormData中保存的对象 。
上传数据data直接为formData实例即可,无需通过JSON封装,否则后端无法解析!!!
上传文件一定是POST请求,所以后端也是用@RequestBody注解解析请求数据
部分upload组件传入的属性含义如下:
action:文件上传的请求
auto-upload:文件导入后是否自动上传
limit:限制文件上传的个数
后端:
1. 配置解析器
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.multipart.MultipartResolver;
@Configuration
public class WebMvcConfig {
@Bean
public MultipartResolver multipartResolver() {
// 使用StandardServletMultipartResolver作为MultipartResolver实现
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
// 可以进一步配置其他属性,例如文件大小限制等,但通常这些在web.xml或通过@MultipartConfig注解在Servlet上进行配置
return multipartResolver;
}
}
2. 在启动器中初始化解析器的相关信息
配置MultiPartConfigElement这一步很重要!!!因为缺了这一步所以卡了一整天555
具体可参考官方文档:StandardServletMultipartResolver (Spring Framework 6.1.8 API)
/**
* 自定义Servlet注册时的配置,特别是针对文件上传的配置。
* 此方法设置上传文件的存储位置、最大文件大小、最大请求大小和文件大小阈值。
*
* @param registration Servlet注册的动态对象,允许在注册时进行额外的配置。
*/
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// 设置多部分配置元素,包括文件上传的临时存储路径、最大文件大小、最大请求大小和写入磁盘的阈值。
registration.setMultipartConfig(new MultipartConfigElement("D:\\coding\\javawebcoding\\javaweb_official_page\\src\\main\\webapp\\img", 1024 * 1024 * 10, 1024 * 1024 * 50, 1024 * 1024));
}
3. 配置处理图片上传请求的controller类及handler方法
@RestController
@CrossOrigin(origins = "http://localhost:5173")
// 对解析器进行相关配置
@MultipartConfig(maxFileSize = 1024 * 1024 * 10, maxRequestSize = 1024 * 1024 * 50)
public class FileController {
// 文件保存路径:webapp/img
private static final String UPLOAD_DIR = "D:\\coding\\javawebcoding\\javaweb_official_page\\src\\main\\webapp\\img"; // 图片保存的目录相对路径
/**
* 处理图片上传请求。
*
* @param file 用户上传的图片文件,以MultipartFile形式接收。
* @return 返回一个Result对象,包含上传结果的状态和消息。
* 如果上传成功,返回包含成功消息和文件名的结果;
* 如果上传失败,返回包含失败原因的结果。
*/
@RequestMapping("/img/upload")
public Result handleImageUpload(@RequestBody MultipartFile file) {
// 检查上传的文件是否为空
if (file == null || file.isEmpty()) {
return Result.failed("图片上传失败:" + "图片为空");
}
try {
// 读取上传文件的字节
byte[] bytes = file.getBytes();
// 构建保存路径
Path path = Paths.get(UPLOAD_DIR + File.separator + file.getOriginalFilename());
// 将文件字节写入指定路径
Files.write(path, bytes);
return Result.success("图片上传成功:" + file.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace();
// 捕获并处理IO异常,返回失败结果
return Result.failed("图片上传失败:" + e.getMessage());
}
}
}
随后文件上传便可以加载到webapp/img中啦!在tomcat设置中on frame deactivation为Update Resource则不需要重新部署文件,便可以从外部通过路径访问新上传的图片。当然通过路径访问静态资源也需要设置相关配置:
//开启静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}