• 前后端分离同步/异步实现文件上传(boot+vue+elementUI)


    前后端分离文件上传

    环境

    后端:Springboot

    前端:vue+elementUI+axios

    上传方法

    elementUI中,文件上传的控件,同步异步两种上传
    文件上传控件,设计的机制就是异步上传
    我们之前学的文件上传都是同步

    同步:将文件和其他表单信息一起提交到控制器,在控制器中完成对象的保存操作,并且将文件存放于某个目录下,数据存储文件的访问路径

    异步
    应用场景:当网站负荷本身就高,再去做文件的读写操作,对于CPU和内存是一种很大的消耗
    用户大量进行上传文件操作,压力全部都在控制器
    通常会用两次请求来完成图片的提交:
    第一次请求:请求到主应用服务器中,用来申请一个token令牌允许用户将文件进行上传,并且将文件的存放地址一并响应的前端
    第二次请求:将图片内容存储到另一个副服务器(OSS)中

    同步实现:

    前端

    注:非完整表单,去饿了么UI直接展开CV大法即可

    1. el-upload空间 设置:auto-upload=“false”
    <el-form-item label="用户头像">
            <el-upload class="avatar-uploader" action="#" 
              :show-file-list="false"
              :on-change="showImage"
              :auto-upload="false">
              <img v-if="imageUrl" :src="imageUrl" class="avatar">
              <i v-else class="el-icon-plus avatar-uploader-icon" style="line-height:178px">i>
            el-upload>
    el-form-item>
    
     <el-button type="primary" @click="submitForm('actrules')">提交el-button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 通过:on-change 进行图片的预览,以及直接将文件的raw属性传递给提前定义的一个变量,改变了类型为对象类型({})
     //上传图片 触发on-change
        showImage(f){
          //渲染照片
          this.imageUrl=URL.createObjectURL(f.raw);
          console.log(f.raw);
          this.file=f.raw;
        },
      },
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 提交事件
       //提交表单
          submitForm () {
          this.$refs.actrules.validate((valid) => {
            if (valid) {
             let param=new FormData(); //创建form对象
             param.append("file",this.file);
             let config={
              Headers:{"Content-Type":"multipart/form-data"},
             }
    
             this.$axios.post("http://localhost:8888/add",param,config).then((response)=>{
              console.log(response.data);
             })
              this.$message.success("成功")
            } else {
              console.log('error submit!!');
              this.$message.error("请填写完表单")
              return false;
            }
          });
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    后端

    注:前后端分离需要解决跨域问题,在Controller上打注解 :@CrossOrigin(origins = {“*”})

    @Value("${image.savepath}")  
        String savePath;
    
    @PostMapping("/add")
        public String add(@RequestParam(value = "file") MultipartFile file) {
            //获取后缀
            String suffix=file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            //使用UUID重新生成文件名 防重名覆盖
            String newName=UUID.randomUUID().toString()+suffix;
            //文件路径
            String imagePath=this.savePath+newName;
    
            try {
                //将文件发送到某个绝对路径上
                file.transferTo(new File(imagePath));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return imagePath;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    异步实现

    1. action向后端请求token,成功触发on-success方法
     <el-form-item label="商品图片">
            <el-upload class="avatar-uploader" action="http://localhost:8888/good/getToken" :show-file-list="false"
              :on-success="getImageToken">
              <img v-if="imgUrl" :src="imgUrl" class="avatar">
              <i v-else class="el-icon-plus avatar-uploader-icon" style="line-height:176px">i>
            el-upload>
          el-form-item>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 只有action请求成功才会触发on-success方法
    //将action中请求的token和file一同发送到文件服务器中 
    getImageToken(response,file){
          console.log(response);
          this.imgUrl=URL.createObjectURL(file.raw);
       	  this.file=file.raw;
          //将图片上传到另一个应用中(非主应用)
          let param=new FormData();
          param.append("file",file.raw);
          param.append("token",response.token)
          let config={
            Headers:{"Content-Type":"multipart/form-data"}
          }
          this.$axios.post("http://localhost:8889/another/upload",param,config).then((response)=>{
            console.log(response.data);
          })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 提交表单 到主程序
    onSubmit(){
          this.$refs.goodForm.validate((valid) => {
            if (valid) {
              // alert('submit!');
              let param = new FormData(); //创建form对象
              param.append("file", this.file);
    
              let config = {
                Headers: { "Content-Type": "multipart/form-data" },
              }
    
                //保存
              this.$axios.post("http://localhost:8888/add", param, config).then((response) => {
                console.log(response.data);
                this.$message.success("成功")
              })
            } else {
              console.log('error submit!!');
              this.$message.error("请填写完表单")
              return false;
            }
          });
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 获取token
     @RequestMapping("/getToken")
        public Imagetoken getToken(){
            System.out.println("获取令牌");
            String token= UUID.randomUUID().toString().replaceAll("-", "");
            Imagetoken it = new Imagetoken();
            it.setToken(token);
            imagetokenService.save(it);
            return it;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 图片服务器:8889,获取token令牌成功后,方可将图片存储,并将存储地址响应给前端
    	
    	@Autowired
        ImagetokenService imagetokenService;
    	@Value("${image.savepath}")
        String savePath;
        @Value("${image.accesspath}")
        String accessPath;  
    
    /*
         * @param token 从前端传来的允许上传的令牌
         * @param file  文件
         * @return
         */
        @PostMapping("/another/upload")
        public Map<String,Object> upload(@RequestParam("token") String token, @RequestParam("file") MultipartFile file){
    
            HashMap<String, Object> result = new HashMap<>();
            try {
                //令牌校验
                //条件构造器,查询指定token
                LambdaQueryWrapper<Imagetoken> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(Imagetoken::getToken,token);
    
                Imagetoken it = imagetokenService.getOne(queryWrapper);
                //如果获取到token令牌
                if(!Objects.isNull(it)){
                    //获取后缀
                    String suffix=file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
                    //UUID重新生成文件名
                    String newName= UUID.randomUUID().toString()+suffix;
    
                    //设置文件路径
                    String savePath=this.savePath+newName;
                    String accessPath=this.accessPath+newName;
    
                    //保存文件
                    file.transferTo(new File(savePath));
    
                    //仅为模拟,懒得新建R
                    result.put("code",200);
                    result.put("savePath",savePath);
                    result.put("accrssPath",accessPath);
                }else {
                    //没有获取到token
                    result.put("code",401);
                    result.put("message","非法请求");
                }
    
            } catch (IOException e) {
                e.printStackTrace();
                result.put("code", 500);
                result.put("message", "请求失败,请重新尝试");
            }
            return result;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    1. 存储服务:8888 主程序
    @Value("${image.savepath}")  
        String savePath;
    
    @PostMapping("/add")
        public String add(@RequestParam(value = "file") MultipartFile file) {
            //获取后缀
            String suffix=file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
            //使用UUID重新生成文件名 防重名覆盖
            String newName=UUID.randomUUID().toString()+suffix;
            //文件路径
            String imagePath=this.savePath+newName;
    
            try {
                //将文件发送到某个绝对路径上
                file.transferTo(new File(imagePath));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return imagePath;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    数据结构-期末复习重要知识点总结
    Linux 装机必备
    软件测试刚入职测不出bug怎么办?
    郑州分销小程序开发分销结算设置介绍
    配置管理新纪元:Eureka引领分布式服务配置潮流
    3.1 基于vexpress-a9 arm平台 的QEMU仿真的kernel镜像环境搭建
    2023秋招--梦加网络--游戏客户端--二面面经
    P5661 [CSP-J2019] 公交换乘
    wps-文档-js宏-批量修改表格格式
    AI绘画普及课【二】图生图
  • 原文地址:https://blog.csdn.net/qq_45925787/article/details/126131489