二十三 、 文件上传

实现效果:用户点击上传按钮、选择上传的头像,确定自动上传,将上传的文件保存到指定的目录中,并重新命名,生成访问链接,返回给前端进行回显。

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的时候不会携带token
      token: true,
      // 开发模式下使用自定义的接口
      customDev: true,
      // 是否开启全局请求loading
      loading: false,
      // 设置请求头
    headers: {
      'Content-Type': 'multipart/form-data'
    }
    })
  }

  export interface FileUrl {
    data: string
  }

2、后端实现

配置拦截器的静态资源映射,方便对上传后的文件进行访问

/**
 * 配置拦截器的静态资源映射
 */
@Slf4j
@Configuration
public class ResourConfigure implements WebMvcConfigurer {
   
     // 静态资源映射
    @Override
    public 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;

    @Autowired
    private 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 {

    @Autowired
    private 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
    @Autowired
    private final UserMapper userMapper;

     //注入写好的工具类
    @Autowired
    private FileUpload fileUpload;

    /**
     * 修改用户头像
     * @param file
     * @return
     */
    @Override
    public 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、测试与效果

上传后的图片

在这里插入图片描述

生成的访问链接,存入到数据库的样子

在这里插入图片描述

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部