课程管理-course_pic上传课程图片接口

上传图片开发

需求分析

在很多系统都有上传图片/上传文件的需求,比如:上传课程图片、上传课程资料、上传用户头像等,为了提供系 统的可重用性专门设立文件系统服务承担图片/文件的管理,文件系统服务实现对文件的上传、删除、查询等功能 进行管理。 各

各子系统不再开发上传文件的请求,各各子系统通过文件系统服务进行文件的上传、删除等操作。文件系统服务 最终会将文件存储到fastDSF文件系统中。

下图是各各子系统与文件系统服务之间的关系:

1566290105521

说明:上述简而言之就是,搭建好fastdfs图片服务器后,由于本项目有多个系统,每个系统都可能要使用文件上传和下载功能,但是每个系统具体的业务明细又不同,比如:课程图片只用一张,各系统可能要传递额外的一些参数

1566290131374

说明:上图就是上述问题的的解决办法,创建一个通用的文件上传和下载工程,实体类包含了上传文件的必要信息,以及各系统可能要传的额外数据组建一个实体类,设置通用的上传和下载方法,对外暴露接口 这样,课程管理系统可以调用通用接口上传,之后在调用方法将图片信息存到自己的数据库中供以后使用

也就是通用文件系统和其他文件系统没有关联,只是提供服务

执行流程如下:

1、管理员进入教学管理前端,点击上传图片

2、图片上传至文件系统服务,文件系统请求fastDFS上传文件

3、文件系统将文件入库,存储到文件系统服务数据库中。

4、文件系统服务向前端返回文件上传结果,如果成功则包括文件的Url路径。

5、课程管理前端请求课程管理进行保存课程图片信息到课程数据库。

6、课程管理服务将课程图片保存在课程数据库。

创建文件系统服务工程

导入xc-service-base-fifilesystem.zip工程。

1)工程目录结构

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>xc-framework-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../xc-framework-parent/pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>xc-service-base-filesystem</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-service-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>net.oschina.zcx7878</groupId>
            <artifactId>fastdfs-client-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
</project>

2)配置文件

原测试程序中fastdfs-client.properties的配置信息统一放在application.yml

application.yml

server:
  port: 22100
spring:
  application:
    name: xc-service-base-filesystem
#mongo配置
  data:
    mongodb:
      database: xc_fs
      uri: mongodb://root:123456@127.0.0.1:27017
#SpringMVC上传文件配置
  servlet:
    multipart:
      #默认支持文件上传.
      enabled: true
      #支持文件写入磁盘.
      file-size-threshold: 0
      # 上传文件的临时目录
      location:
      # 最大支持文件大小
      max-file-size: 2MB
      # 最大支持请求大小
      max-request-size: 40MB
xuecheng:
  fastdfs:
    connect_timeout_in_seconds: 20
    network_timeout_in_seconds: 30
    charset: UTF-8
    tracker_servers: 192.168.138.129:22122 #多个 trackerServer中间以逗号分隔

API接口

模型类

系统的文件信息(图片、文档等小文件的信息)在mongodb中存储,下边是文件信息的模型类。

1) 模型如下:

package com.xuecheng.framework.domain.filesystem;

import lombok.Data;
import lombok.ToString;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.Map;

/**
 * 文件系统实体类
 * Created by mrt on 2018/2/5.
 */
@Data
@ToString
@Document(collection = "filesystem")
public class FileSystem {

    @Id
    private String fileId;
    //文件请求路径
    private String filePath;
    //文件大小
    private long fileSize;
    //文件名称
    private String fileName;
    //文件类型
    private String fileType;
    //图片宽度
    private int fileWidth;
    //图片高度
    private int fileHeight;
//    外加一些通用的属性,在其他服务调用时可以用到,因为很多文件系统都会用到这个文件系统服务,
//    外加一些属性备用,可以让其他业务系统能存自己所需信息
    //用户id,用于授权
    private String userId;
    //业务key
    private String businesskey;
    //业务标签
    private String filetag;
    //文件元信息
    private Map metadata;
}

说明:

fifileId:fastDFS返回的文件ID。

fifilePath:请求fastDFS浏览文件URL。

fifiletag:文件标签,由于文件系统服务是公共服务,文件系统服务会为使用文件系统服务的子系统分配文件标签,

用于标识此文件来自哪个系统。

businesskey:文件系统服务为其它子系统提供的一个业务标识字段,各子系统根据自己的需求去使用,比如:课

程管理会在此字段中存储课程id用于标识该图片属于哪个课程。

metadata:文件相关的元信息。

2) collection

在mongodb创建数据库xc_fs(文件系统数据库),并创建集合 fifilesystem。

这里可以直接先创建数据库,后面代码写好写入可以自动生成文档

Api接口

在api工程下创建com.xuecheng.api.fifilesystem包,

package com.xuecheng.api.fileSystem;

import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.multipart.MultipartFile;

/**
 * 文件系统通用接口
 * @author Hr|黄锐
 * @create 2019/8/18 20:47
 */
@Api(value = "文件管理接口",description = "文件管理接口,提供文件的上传于下载")
public interface FileSystemControllerApi {
    //上传文件
    @ApiOperation("上传文件")
    public UploadFileResult uploadFile(
            MultipartFile multipartFile,
            String fileTag,
            String businessKey,
            String metadata//传过来的为json数据,先用String,再转map存储
    );
}

Dao

将文件信息存入数据库,主要存储文件系统中的文件路径。

public interface FileSystemRepository extends MongoRepository<FileSystem,String> { 
}

Service

package com.xuecheng.filesystem.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.filesystem.dao.FileSystemRepository;
import com.xuecheng.framework.domain.filesystem.FileSystem;
import com.xuecheng.framework.domain.filesystem.response.FileSystemCode;
import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import org.apache.commons.lang3.StringUtils;
import org.csource.fastdfs.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

/**
 * @author Hr|黄锐
 * @create 2019/8/18 20:59
 */
@Service
public class FileSystemService {
//注入配置文件属性
    @Value("${xuecheng.fastdfs.tracker_servers}")
    String tracker_servers;
    @Value("${xuecheng.fastdfs.connect_timeout_in_seconds}")
    int connect_timeout_in_seconds;
    @Value("${xuecheng.fastdfs.network_timeout_in_seconds}")
    int network_timeout_in_seconds;
    @Value("${xuecheng.fastdfs.charset}")
    String charset;

    @Autowired
    FileSystemRepository fileSystemRepository;

    //上传文件
    public UploadFileResult uploadFile(
            MultipartFile multipartFile,
           String fileTag,
           String businessKey,
           String metadata) {
        if (multipartFile == null) {
            ExceptionCast.cast(FileSystemCode.FS_UPLOADFILE_FILEISNULL);
        }
        //将文件上传到fastdfs
        String fileId = this.uploadFastdfs(multipartFile);
        if (StringUtils.isEmpty(fileId)) {
            ExceptionCast.cast(FileSystemCode.FS_UPLOADFILE_SERVERFAIL);
        }
        //将文件id与其他信息存储到mongodb中
        FileSystem fileSystem = new FileSystem();
        fileSystem.setFileId(fileId);
        fileSystem.setFilePath(fileId);
        fileSystem.setFiletag(fileTag);
        fileSystem.setBusinesskey(businessKey);
        fileSystem.setFileName(multipartFile.getOriginalFilename());
        fileSystem.setFileType(multipartFile.getContentType());
        if (StringUtils.isEmpty(metadata)) {
            try {
//                将json转为map
            Map map = JSON.parseObject(metadata, Map.class);
            fileSystem.setMetadata(map);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        fileSystem.setFileSize(multipartFile.getSize());
        fileSystemRepository.save(fileSystem);
        return new UploadFileResult(CommonCode.SUCCESS, fileSystem);

    }

    //    将文件上传到fastdfs
    /**
     *
     * @param multipartFile 文件本身
     * @return  文件id
     */
    private String uploadFastdfs(MultipartFile multipartFile) {
        try {
            //初始化fastdfs环境
            initFdfsConfig();
            //创建一个trackerClient
            TrackerClient trackerClient = new TrackerClient();
            TrackerServer trackerServer = trackerClient.getConnection();
            //得到storage服务器
            StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);
            StorageClient1 storageClient1 = new StorageClient1(trackerServer, storageServer);
            //上传文件
            //得到原始名称
            String originalFilename = multipartFile.getOriginalFilename();
            //获取扩展名
            String type = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            //获取字节文件
            byte[] bytes = multipartFile.getBytes();
            String fileId = storageClient1.upload_file1(bytes, type, null);
            return fileId;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //初始化fastdfs环境
    private void initFdfsConfig() {
        try {
        //初始化tracker服务器,多个tracker以半角逗号分隔
            ClientGlobal.initByTrackers(tracker_servers);
            ClientGlobal.setG_charset(charset);
            ClientGlobal.setG_network_timeout(network_timeout_in_seconds);
            ClientGlobal.setG_connect_timeout(connect_timeout_in_seconds);
        } catch (Exception e) {
            e.printStackTrace();
            //初始化出错,抛出异常
            ExceptionCast.cast(FileSystemCode.FS_INITFDFSERROR);
        }
    }
}

说明:(1) 由于这是springboot工程所以这里配置文件是yaml格式,读取文件时使用Value注解的方式诸如配置信息

Controller

package com.xuecheng.filesystem.controller;

import com.xuecheng.api.fileSystem.FileSystemControllerApi;
import com.xuecheng.filesystem.service.FileSystemService;
import com.xuecheng.framework.domain.filesystem.response.UploadFileResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author Hr|黄锐
 * @create 2019/8/18 20:58
 */
@RestController
@RequestMapping("/filesystem")
public class FileSystemController implements FileSystemControllerApi {
    @Autowired
    FileSystemService fileSystemService;

    /**
     * 上传文件
     * @param multipartFile
     * @param fileTag
     * @param businessKey
     * @param metadata
     * @return
     */
    @Override
    @PostMapping("/upload")
    /*
    *这里一定注意在进行前后端联调时,开始当我们选择文件上传时,会报错,原因是
    * 前端传过来的文件名称默认为file,饿了么ui默认的,但是我们测试时Springmvc框架默认
    * 参数为multipartFile,所以我们要在前端upload组件上加上name="multipartFile"属性才行
    *
    * 或者我们在后端的参数上加上@RequestParam("File")也行,
    * 总之,前端的name要和后端@RequestParam参数名相同
     */
    public UploadFileResult uploadFile(
           @RequestParam(value = "multipartFile") MultipartFile multipartFile,
           @RequestParam(value = "filetag",required = false) String fileTag,
           @RequestParam(value = "businessKey",required = false) String businessKey,
           @RequestParam(value = "metadata",required = false) String metadata) {
        return fileSystemService.uploadFile(multipartFile, fileTag, businessKey, metadata);
    }
}

上传课程图片前端

需求

点击“加号”上传图片,图片上传成功自动显示;点击“删除”将删除图片。

页面

使用Element-UI的Upload上传组件实现上边的效果。

1) template

<el-upload
      action="/api/filesystem/upload"
      list-type="picture-card"
      :before-upload="setuploaddata"
      :on-success="handleSuccess"
      :file-list="fileList"
      :limit="picmax"
      :on-exceed="rejectupload"
      :before-remove="handleRemove"
      :data="uploadval"
      name="multipartFile">
      <i class="el-icon-plus"></i>
    </el-upload>

el-upload参数说明:

action:必选参数,上传的地址

list-type:文件列表的类型(text/picture/picture-card)

before-upload:上传前执行钩子方法 ,function(fifile)

on-success:上传成功 执行的钩子方法 ,function(response, fifile, fifileList)

on-error:上传失败的钩子方法,function(err, fifile, fifileList)

on-remove:文件删除的钩子方法,function(fifile, fifileList)

fifile-list:文件列表,此列表为上传成功 的文件

limit:最大允许上传个数

on-exceed:文件超出个数限制时的钩子,方法为:function(fifiles, fifileList)

data:提交上传的额外参数,需要封装为json对象,最终提交给服务端为key/value串

2)数据模型

<script>
  import * as sysConfig from '@/../config/sysConfig';
  import * as courseApi from '../../api/course';
  import utilApi from '../../../../common/utils';
  import * as systemApi from '../../../../base/api/system';
  export default {
    data() {
      return {
        picmax:1,//最大上传文件的数量
        courseid:'',
        dialogImageUrl: '',
        dialogVisible: false,
        fileList:[],
        uploadval:{filetag:"course",businessKey:"testBusinesskey"},//上传提交的额外的数据 ,将uploadval转成key/value提交给服务器
        imgUrl:sysConfig.imgUrl
      }
    },
    methods: {
      //超出文件上传个数提示信息
      rejectupload(){
        this.$message.error("最多上传"+this.picmax+"个图片");
      },
      //在上传前设置上传请求的数据
      setuploaddata(){

      },
      //删除图片
      handleRemove(file, fileList) {
        console.log(file)
        //调用服务端去删除课程图片信息,如果返回false,前端停止删除
        //异步调用
        return new Promise((resolve,rejct)=>{
          courseApi.deleteCoursePic(this.courseid).then(res=>{
            if(res.success){
              this.$message.success("删除成功");
                //成功
              resolve()
            }else{
              this.$message.error("删除失败");
                //失败
              rejct()
            }

          })
        })

      },
      //上传成功的钩子方法
      handleSuccess(response, file, fileList){
        console.log(response)
        //调用课程管理的保存图片接口,将图片信息保存到课程管理数据库course_pic中
        //从response得到新的图片文件的地址
        if(response.success){
          let fileId = response.fileSystem.fileId;
          courseApi.addCoursePic(this.courseid,fileId).then(res=>{
              if(res.success){
                  this.$message.success("上传图片成功")
              }else{
                this.$message.error(res.message)
              }

          })
        }

      },
      //上传失败执行的钩子方法
      handleError(err, file, fileList){
        this.$message.error('上传失败');
        //清空文件队列
        this.fileList = []
      },
      //promise 有三种状态:
      //进行中pending
      //执行成功 resolve
      //执行失败 reject
      testPromise(i){

          return new Promise((resolve,reject)=>{
              if(i<2){
                  //成功了
                resolve('成功了');
              }else{
                  //失败了
                reject('失败了');
              }

          })
      }
    },
    mounted(){
      //课程id
      this.courseid = this.$route.params.courseid;
      //查询课程
      courseApi.findCoursePicList(this.courseid).then(res=>{
          if(res && res.pic){
              let imgUrl = this.imgUrl+res.pic;
              //将图片地址设置到
            this.fileList.push({name:'pic',url:imgUrl,fileId:res.pic})
          }

      })
      //测试调用promise方法,then中写的成功后的回调方法,
//      this.testPromise(3).then(res=>{
//          alert(res)
//      }).catch(res=>{//catch就是执行失败的回调方法
//          alert("失败了。。。。。")
//          alert(res)
//      })
    }
  }
</script>

保存课程图片

需求分析

图片上传到文件系统后,其它子系统如果想使用图片可以引用图片的地址,课程管理模块使用图片的方式是将图片

地址保存到课程数据库中。

业务流程如下:

1、上传图片到文件系统服务

2、保存图片地址到课程管理服务

在课程管理服务创建保存课程与图片对应关系的表 course_pic。

1566290150375

3、在course_pic保存图片成功后方可查询课程图片信息。

通过查询course_pic表数据则查询到某课程的图片信息。

课程管理服务端开发

API

课程管理需要使用图片则在课程管理服务中要提供保存课程图片的api。

@ApiOperation("添加课程图片") 
public ResponseResult addCoursePic(String courseId,String pic);

Dao

模型:

@Data 
@ToString 
@Entity 
@Table(name="course_pic") 
@GenericGenerator(name = "jpa‐assigned", strategy = "assigned") 
public class CoursePic implements Serializable { 
    private static final long serialVersionUID = ‐916357110051689486L; 
    @Id
    @GeneratedValue(generator = "jpa‐assigned") 
    private String courseid; 
    private String pic; 
}

API如下:

public interface CoursePicRepository extends JpaRepository<CoursePic, String> { 
}

Service

/**
     * 添加课程id与fastdfs图片信息到课程管理数据库
     * @param courseId
     * @param picId
     * @return
     */
    @Transactional
    public ResponseResult addCoursePic(String courseId, String picId) {
        /*
        *由于课程管理数据库每个课程只能有一个图片,主键就是courseId,所以先查询是否存在该课程图片信息,
        * 没有就创建对象,有的话就更新
         */
        //课程图片信息
        CoursePic coursePic = null;
        //查询课程图片
        Optional<CoursePic> optional = coursePicRepository.findById(courseId);
        if (optional.isPresent()) {
            coursePic = optional.get();
        }
        if (coursePic == null) {
             coursePic = new CoursePic();
        }
        coursePic.setCourseid(courseId);
        coursePic.setPic(picId);
        coursePicRepository.save(coursePic);
        return new ResponseResult(CommonCode.SUCCESS);
    }

在前端执行完成上传图片之后,调用钩子方法执行此接口,将信息存到自己得数据库中.

Controller

@Override 
@PostMapping("/coursepic/add") 
public ResponseResult addCoursePic(
    @RequestParam("courseId") String courseId, 
    @RequestParam("pic") String pic) { 
    //保存课程图片 
    return courseService.saveCoursePic(courseId,pic); 
}

前端开发

前端需要在上传图片成功后保存课程图片信息。

Api方法

//添加课程图片 
export const addCoursePic= (courseId,pic) => { 
    return http.requestPost(apiUrl+'/course/coursepic/add?courseId='+courseId+"&pic="+pic) 
}

页面

1)添加上传成功的钩子 :on-success=“handleSuccess”

<el-upload 
           action="/api/filesystem/upload" 
           list-type="picture-card" 
           :on-success="handleSuccess"> 
    <i class="el-icon-plus"></i> 
</el-upload>

2)在钩子方法 中保存课程图片信息

如果保存图片失败则上传失败,清除文件列表。

 //上传成功的钩子方法
      handleSuccess(response, file, fileList){
        console.log(response)
        //调用课程管理的保存图片接口,将图片信息保存到课程管理数据库course_pic中
        //从response得到新的图片文件的地址
        if(response.success){
          let fileId = response.fileSystem.fileId;
          courseApi.addCoursePic(this.courseid,fileId).then(res=>{
              if(res.success){
                  this.$message.success("上传图片成功")
              }else{
                this.$message.error(res.message)
              }

          })
        }
      },
    //上传失败执行的钩子方法
          handleError(err, file, fileList){
            this.$message.error('上传失败');
            //清空文件队列
            this.fileList = []
          },

图片查询

需求分析

课程图片上传成功,再次进入课程上传页面应该显示出来已上传的图片。

API

在课程管理服务定义查询方法

@ApiOperation("获取课程基础信息") 
public CoursePic findCoursePic(String courseId);

课程管理服务开发

Dao

使用CoursePicRepository即可,无需再开发。

Service

根据课程id查询课程图片

public CoursePic findCoursepic(String courseId) {
    return coursePicRepository.findOne(courseId); 
}

Controller

@Override @GetMapping("/coursepic/list/{courseId}") 
public CoursePic findCoursePic(
    @PathVariable("courseId") String courseId) { 
    return courseService.findCoursepic(courseId); 
}

前端开发

API方法

//查询课程图片 
export const findCoursePicList = courseId => { 
    return http.requestQuickGet(apiUrl+'/course/coursepic/list/'+courseId) 
}

页面

在课程图片页面的mounted钩子方法 中查询课程图片信息,并将图片地址赋值给数据对象

1、定义图片查询方法

 //课程id
      this.courseid = this.$route.params.courseid;
//查询课程
      courseApi.findCoursePicList(this.courseid).then(res=>{
          if(res && res.pic){
              let imgUrl = this.imgUrl+res.pic;
              //将图片地址设置到
            this.fileList.push({name:'pic',url:imgUrl,fileId:res.pic})
          }

      })

在mounted中调用,直接进入页面就查询

测试

测试流程:

1、上传图片成

2、进入上传图片页面,观察图片是否显示

课程图片删除

需求分析

课程图片上传成功后,可以重新上传,方法是先删除现有图片再上传新图片。

注意:此删除只删除课程数据库的课程图片信息,不去删除文件数据库的文件信息及文件系统服务器上的文件,由

于课程图片来源于该用户的文件库,所以此图片可能存在多个地方共用的情况,所以要删除文件系统中的文件需要

到图片库由用户确认后再删除。

API

在课程管理服务添加删除课程图片api:

@ApiOperation("删除课程图片") 
public ResponseResult deleteCoursePic(String courseId);

课程管理服务端开发

Dao

CoursePicRepository父类提供的delete方法没有返回值,无法知道是否删除成功,这里我们在

CoursePicRepository下边自定义方法:

//删除成功返回1否则返回0 
long deleteByCourseid(String courseid);

Service

//删除课程图片 
@Transactional 
public ResponseResult deleteCoursePic(String courseId) { 
    //执行删除,返回1表示删除成功,返回0表示删除失败 
    long result = coursePicRepository.deleteByCourseid(courseId); 
    if(result>0){ 
        return new ResponseResult(CommonCode.SUCCESS); 
    }
    return new ResponseResult(CommonCode.FAIL); 
}

Controller

@Override 
@DeleteMapping("/coursepic/delete") 
public ResponseResult deleteCoursePic(
    @RequestParam("courseId") String courseId) { 
    return courseService.deleteCoursePic(courseId); 
}

前端开发

API 调用

//删除课程图片 
export const deleteCoursePic= courseId => { 
    return http.requestDelete(apiUrl+'/course/coursepic/delete?courseId='+courseId) 
}

页面测试

1)before-remove钩子方法

在upload组件的before-remove钩子方法 中实现删除动作。

<el‐upload 
           action="/filesystem/upload" 
           list‐type="picture‐card" 
           :before‐remove="handleRemove"> 
    <i class="el‐icon‐plus"></i> 
</el‐upload>

before-remove说明:删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除。

定义handleRemove方法进行测试:

handleRemove 返回true则删除页面的图片,返回false则停止删除页面的图片。

//删除图片 
handleRemove(file, fileList) { 
    console.log(file) alert('删除成功') 
        return true; 
}

promise异步调用

在handleRemove方法调用删除图片的api方法,删除成功时return true,删除失败时return false;

//删除图片 
handleRemove(file, fileList) { console.log(file) 
    // alert('删除') 
    // return true; 
    //删除图片 
    courseApi.deleteCoursePic('1').then((res) => { 
    if(res.success){ 
        this.$message.success('删除成功'); 
        return true; 
    }else{ 
        this.$message.error(res.message); 
        return false; 
    } 
}); 
},

在上边代码中将提交的课程id故意写错,按照我们预期应该是删除失败,而测试结果却是图片在页面上删除成功。

问题原因:

通过查询deleteCoursePic方法的底层代码,deleteCoursePic最终返回一个promise对象。

Promise是ES6提供的用于异步处理的对象,因为axios提交是异步提交,这里使用promise作为返回值。

Promise的使用方法如下:

Promise对象在处理过程中有三种状态:

pending:进行中

resolved:操作成功

rejected: 操作失败

Promise的构建方法如下:

const promise = new Promise(function(resolve,reject){ 
    //...TODO... 
    if(操作成功){ 
        resolve(value); 
    }else{
        reject(error); 
    } 
})

上边的构造方法function(resolve,reject)执行流程如下:

1)方法执行一些业务逻辑。

2)如果操作成功将Promise的状态由pending变为resolved,并将操作结果传出去

3)如果操作失败会将promise的状态由pending变为rejected,并将失败结果传出去。

上边说的操作成功将操作结果传给谁了呢?操作失败将失败结果传给谁了呢?

通过promise的then、catch来指定

promise.then(function (result) { 
    console.log('操作成功:' + result); 
}); 
promise.catch(function (reason) { c
    onsole.log('操作失败:' + reason); 
});

例子如下:

1、定义一个方法,返回promise对象

this.testPromise(3).then(res=>{
          alert(res)
      }).catch(res=>{//catch就是执行失败的回调方法
          alert("失败了。。。。。")
          alert(res)
      })

2、调用此方法

向方法传入偶数、奇数进行测试。

this.testpromise(3).then(res=>{
    //在then中对成功结果进行处理 
    alert(res) 
}).catch(res=>{
    //在catch中对操作失败结果进行处理 
    alert(res) 
})

3、最终将handleRemove方法修改如下

handleRemove方法返回promise对象,当删除成功则resolve,删除失败则reject。

 //删除图片
      handleRemove(file, fileList) {
        console.log(file)
        //调用服务端去删除课程图片信息,如果返回false,前端停止删除
        //异步调用
        return new Promise((resolve,rejct)=>{
          courseApi.deleteCoursePic(this.courseid).then(res=>{
            if(res.success){
              this.$message.success("删除成功");
                //成功
              resolve()
            }else{
              this.$message.error("删除失败");
                //失败
              rejct()
            }
          })
        })
      },

因此,是用来处理异步调用的,在方法外面包裹一个new promise(){},在里面执行异步调用,成功则调用resolve,否则rejct回退

一般在前端定义通用rest请求接口,都是这样定义的,

如:post请求

//post请求
  requestPost (url, params = {}) {
    return new Promise((resolve, reject) => {
      axios.post(url, params).then(res => {
        resolve(res.data)
      }).catch(error => {
        reject(error)
      })
    })
  },

请求通过执行resolve返回数据,失败执行catch返回错误信息

注意:我们调用postApi时,并没有写catch,这是因为我们后端做了异常处理,无论请求成功与否,都会返回相关信息,所以前段会认为都是请求成功了

HuangRui

Every man dies, not every man really lives.

HaungRui, China suixinblog.cn