二十三 、 文件上传
实现效果:用户点击上传按钮、选择上传的头像,确定自动上传,将上传的文件保存到指定的目录中,并重新命名,生成访问链接,返回给前端进行回显。
1、前端实现
vue3+AntDesignVue实现
<template><!--图片回显--><a-avatar :size="100"><template #icon><img :src="userStore.userInfo.avatar+'?' + new Date().getTime()" alt=""></template></a-avatar><!--图片上传按钮 --><!--showUploadList=fales 不显示上传列表;:multiple="false" 只上传1个;accept=".png,.jpg" 限制文件格式--><a-upload name="file":file-list="fileList":showUploadList="false":beforeUpload="beforeUpload" :onChange="handleChange" :multiple="false" accept=".png,.jpg"><a-button class="mt-4">上传头像</a-button></a-upload></template><script setup lang="ts">
import type { UnwrapRef } from 'vue';
import { updateUserAvatarApi } from '~/api/file/file.ts';const { t } = useI18n()
//文件列表
const fileList = ref([]);//
async function beforeUpload(file){//文件类型,限制,可以不用写,因为,在accept=".png,.jpg" 已经限制了
var fileName = file.name.substring(file.name.lastIndexOf('.') + 1)
if (fileName!='png' && fileName!='jpg') {message.error('文件类型必须是png或jpg!')
return false
}//文件大小(3M=3*1024KB*1024字节)
var fileSize=file.size;
if(fileSize > 3*1024*1024){message.error("图片大小不能大于3M");return false
}try { const formData = new FormData(); formData.append('file', file); // 将文件添加到 FormData 对象中 // 假设updateUserAvatarApi返回的是一个Promise,且解析为包含fileUrl的对象 const response = await updateUserAvatarApi(formData); if(response.data.code==1){fileList.value=[]; //清空文件列表(没用因为在beforeUpload中,我直接showUploadList="false"不显示列表)userStore.userInfo.avatar=response.data.data;message.success('头像上传成功');}else{message.error('头像上传失败,请重试'); }} catch (error) { // 处理上传失败的情况 message.error(error); } // 返回false以阻止<a-upload>的默认上传行为 return false; }// 处理文件上传或移除后的逻辑
function handleChange(info) { // info.fileList 是更新后的文件列表 // 但由于限制了multiple为false,所以这里fileList应该始终只有一个文件或为空 fileList.value = info.fileList.slice(-1);
} </script>
请求函数
// 后面的方法是用户自己头像
export function updateUserAvatarApi(param: any) {return usePost<FileUrl>('/upload/uploadUserAvater', param, {// 设置为false的时候不会携带tokentoken: true,// 开发模式下使用自定义的接口customDev: true,// 是否开启全局请求loadingloading: false,// 设置请求头headers: {'Content-Type': 'multipart/form-data'}})}export interface FileUrl {data: string}
2、后端实现
配置拦截器的静态资源映射,方便对上传后的文件进行访问
/*** 配置拦截器的静态资源映射*/
@Slf4j
@Configuration
public class ResourConfigure implements WebMvcConfigurer {// 静态资源映射@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {/*** 资源映射路径* addResourceHandler:访问映射路径* addResourceLocations:资源绝对路径*/String osName=System.getProperty("os.name");String fileUploadResources="/static/**";String win="win";if(osName.toLowerCase().startsWith(win)){ApplicationHome applicationHome=new ApplicationHome(this.getClass());String pre="file:"+applicationHome.getDir().getParentFile().getParentFile()+ "\\src\\main\\resources\\static\\";registry.addResourceHandler(fileUploadResources).addResourceLocations(pre);}else {ApplicationHome applicationHome=new ApplicationHome(this.getClass());String pre="file:"+applicationHome.getDir().getParentFile().getParentFile()+ "/src/main/resources/static/";registry.addResourceHandler(fileUploadResources).addResourceLocations(pre);}}}
写相关配置,在application.properties中,方便生成访问链接
#根据自己的需求进行修改即可
#端口号
server.port=8080
#服务地址
server.address=localhost
#访问路径
server.servlet.context-path=/
#限制单个文件的上传大小
spring.servlet.multipart.max-file-size=5MB
#限制整个请求的最大大小
spring.servlet.multipart.max-request-size=5MB
创建文件上传工具类,方便对文件上传进行操作。
在此之前,确保这个目录的存在:(\src\main\resources\static\)可根据自己需求进行修改,在工具类中
/*** 文件上传工具类*/
@Component
public class FileUpload {@Value("${server.servlet.context-path}")private String contextPath;@Value("${server.port}")private String serverPort;@Value("${server.address}")private String serverAddress;@Autowiredprivate ServletContext servletContext;public String uploadFile(MultipartFile file, String folder) {// 获取图片的原始名字String originalFilename = file.getOriginalFilename();if (originalFilename == null || originalFilename.isEmpty()) {throw new IllegalArgumentException("文件名不能为空");}// 获取文件的后缀和新文件名String ext = "." + originalFilename.substring(originalFilename.lastIndexOf('.') + 1);String uuid = UUID.randomUUID().toString().replace("-", "");String fileName = uuid + ext;// 构建目标文件路径String pre = getResourcePath(folder);String filePath = pre + fileName;// 上传图片try {file.transferTo(new File(filePath));// 返回访问链接return getAccessPath(folder, fileName);} catch (IOException e) {e.printStackTrace();}return null;}public String uploadFile(MultipartFile file, String folder, String fileName) {// 获取图片的原始名字String originalFilename = file.getOriginalFilename();if (originalFilename == null || originalFilename.isEmpty()) {throw new IllegalArgumentException("文件名不能为空");}// 获取文件的后缀和新文件名String ext = "." + originalFilename.substring(originalFilename.lastIndexOf('.') + 1);fileName = fileName + ext;// 构建目标文件路径String pre = getResourcePath(folder);String filePath = pre + fileName;// 上传图片try {file.transferTo(new File(filePath));// 返回访问链接return getAccessPath(folder, fileName);} catch (Exception e) {e.printStackTrace();}return null;}//获取上传路径private String getResourcePath(String folder) {// 获取操作系统的名称String osName = System.getProperty("os.name");String win = "win";// 获取项目的根目录String projectRoot = System.getProperty("user.dir");// 根据操作系统生成正确的路径String staticPath;if (osName.toLowerCase().startsWith(win)) {//windos系统staticPath = projectRoot + "\\src\\main\\resources\\static\\" + folder + "\\";} else {//Linux系统staticPath = projectRoot + "/src/main/resources/static/" + folder + "/";}// 如果目录不存在,就创建目录File dir = new File(staticPath);if (!dir.exists()) {dir.mkdirs();}return staticPath;}//构建访问路径private String getAccessPath(String folder, String fileName) {// 构建访问路径return "http://" + serverAddress + ":" + serverPort + contextPath + "static/" + folder + "/" + fileName;}
}
可以设置一个常量类,来指定上传到的目录
public class FileUploadFolder {public static final String USER_AVATER_FOLDER = "user_avatar";public static final String ACTIVITY_FOLDER = "activity";
}
后端接口编写:Controller层,根据自己的项目来即可
@Tag(name="文件上传接口")
@RestController
@RequestMapping("/upload")
@Slf4j
public class UploadController {@Autowiredprivate UserService userService;/*** 修改用户头像* @param file* @return*/@Operation(summary = "修改用户头像")@PostMapping("/uploadUserAvater")public ResponseResult uploadUserAvater(@RequestParam("file") MultipartFile file){if(file.isEmpty()){return ResponseResult.error("头像为空,请重新选择");}String imgUrl = userService.uploadUserAvater(file);if(imgUrl==null){return ResponseResult.error("头像上传失败");}return ResponseResult.success(imgUrl);}}
Service层:根据自己的项目来即可
@Slf4j
@Service
@Transactional
public class UserServiceImpl implements UserService {//根据自己的项目来,注入对应的Bean@Autowiredprivate final UserMapper userMapper;//注入写好的工具类@Autowiredprivate FileUpload fileUpload;/*** 修改用户头像* @param file* @return*/@Overridepublic String uploadUserAvater(MultipartFile file) {// 获取用户获取用户信息(根据自己的项目来,获取用户ID,来查询用户修改头像链接)Long uid = Long.parseLong(StpUtil.getLoginId().toString());UserInfo userInfo = userMapper.getUserById(uid);// 调用文件上传工具类,传入:文件,保存到的文件夹,文件名//设置文件名是为了替换旧的头像文件String imgUrl= fileUpload.uploadFile(file, FileUploadFolder.USER_AVATER_FOLDER,userInfo.getUsername());if(imgUrl!=null) {userInfo.setAvatar(imgUrl);userMapper.updateById(userInfo);log.info("头像上传成功:" + imgUrl);//返回生成的链接return imgUrl;}return null;}}
3、测试与效果
上传后的图片
生成的访问链接,存入到数据库的样子