打造一款适合自己的快速开发框架-上传模块设计与实现

1,922 阅读12分钟

前言

一般来说每一个系统都会有上传功能,传统的上传方式主要有如下两种:一种是上传功能与业务数据放在同一个表单一起提交;另一种是独立上传返回url,然后url和业务数据一起提交。两种方式各有优劣,这里也不做比较,在这里采用的是后者。

常见的文件存储方案

  • 应用服务器本地存储
  • ftp服务器
  • 云存储(七牛、阿里云oss等)

文件上传流程

不同的存储方案,上传的流程会有所区别,在这里主要分以下两种:

  • 服务器上传

服务器上传方式是最常见的方式,其主要流程如下(画时序图可能会更直观些,这里就先偷个懒):

  1. 客户端构建文件上传表单
  2. 文件上传到服务器
    1. 如果是本地存储,则直接写入应用服务器本地磁盘
    2. 如果是ftp服务器,则调用ftp api,将文件由应用服务器上传到ftp服务器
    3. 如果是云存储,则调用对应的云存储api,将文件由应用服务器上传到云存储服务器
  3. 返回上传结果
  • 客户端上传(直传)

客户端直接上传,该方式一般只出现在云存储方案中,其主要流程如下(画时序图可能会更直观些,这里就先偷个懒):

第一步:申请上传凭证

  1. 客户端向服务器申请上传凭证
  2. 应用服务器调用云存储api创建上传凭证
  3. 返回上传凭证

第二步:客户端将文件上传至云存储

  1. 客户端构建云存储上传表单
  2. 调用云存储api上传文件
  3. 云存储服务器返回上传结果

通用上传模块设计

当不同的业务使用同一个上传模块时,就需要一些额外的参数做区分,下面简单罗列一下一些关键性参数:

上传参数配置表:

列名 备注
biz_type 业务类型,唯一标识,用以区分不同业务模块的,也通过该参数去获取配置项
file_size_min 限定上传文件大小最小值,单位byte。(0为不限制)
file_size_max 限定上传文件大小最大值,单位byte。(0为不限制)
file_ext 限定用户上传后辍,可查看mime_type对照表
naming_strategy 命名策略

为了方便系统回顾查询浏览,最好要有文件上传记录:

列名 备注
biz_type 业务类型(同上)
biz_id 对应的业务id
url 文件保存的资源路径,由命名策略生成。
file_name 上传的原始文件名。
file_size 资源大小,单位为字节。
mime_type 资源类型,例如JPG图片的资源类型为image/jpg
file_ext 上传资源的后缀名

数据库设计

  • 上传配置表:
CREATE TABLE `sys_upload_config` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `biz_type` varchar(32) NOT NULL COMMENT '业务类型',
  `file_size_min` bigint(20) unsigned DEFAULT '0' COMMENT '限定上传文件大小最小值,单位`byte`。(0为不限制)',
  `file_size_max` bigint(20) unsigned DEFAULT '0' COMMENT '限定上传文件大小最大值,单位`byte`。(0为不限制)',
  `file_ext` varchar(64) NOT NULL COMMENT '限定用户上传后辍(多个逗号分割)',
  `base_url` varchar(32) DEFAULT '' COMMENT '访问地址前辍',
  `callback_url` varchar(100) DEFAULT '' COMMENT '回调地址',
  `naming_strategy` varchar(32) NOT NULL COMMENT '命名策略',
  `is_record` tinyint(1) unsigned NOT NULL DEFAULT '1' COMMENT '是否记录(1->不记录|NO,2->记录|YES)',
  `create_time` datetime(3) NOT NULL COMMENT '创建时间',
  `update_time` datetime(3) NOT NULL COMMENT '更新时间',
  `is_deleted` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除(1->未删除|NO,2->已删除|YES)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上传配置';


  • 上传记录表
CREATE TABLE `sys_upload_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `biz_type` varchar(32) DEFAULT NULL COMMENT '业务类型',
  `biz_id` varchar(40) DEFAULT NULL COMMENT '业务id',
  `url` varchar(100) DEFAULT NULL COMMENT '文件保存的资源路径',
  `file_name` varchar(100) DEFAULT NULL COMMENT '上传的原始文件名',
  `file_size` bigint(20) unsigned DEFAULT NULL COMMENT '资源大小,单位为字节',
  `mime_type` varchar(32) DEFAULT NULL COMMENT '资源类型',
  `file_ext` varchar(10) DEFAULT NULL COMMENT '上传资源的后缀名',
  `create_time` datetime(3) DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(3) DEFAULT NULL COMMENT '更新时间',
  `is_deleted` tinyint(1) unsigned DEFAULT '1' COMMENT '是否删除(1->未删除|NO,2->已删除|YES)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上传记录';

七牛云存储设计

本快速开发框架使用的是七牛云存储,所以需要了解其sdk,这里将关键性api贴出来。

  • 命名策略

命名策略在对应七牛sdk生成上传凭证的key或saveKey

key的方式


String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
String key = "file key";
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket, key);
System.out.println(upToken);

saveKey的方式

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
// 自定义资源名支持魔法变量和自定义变量
putPolicy.put("saveKey", "biz_type/2020/06/13/uuid$(ext)");
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, null, expireSeconds, putPolicy);
System.out.println(upToken); 
  • 上传文件大小限制

上传文件大小限制是七牛上传策略的一种,分别对应着 fsizeMin 和fsizeMax配置项:

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
String key = "file key";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
// 限定上传文件大小最小值
putPolicy.put("fsizeMin", 0);
// 限定上传文件大小最大值
putPolicy.put("fsizeLimit", 1024 * 1024 * 10);
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, key, expireSeconds, putPolicy);
System.out.println(upToken);    
  • 上传文件类型限制

上传文件类型限制也是七牛上传策略的一种

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
// 限制文件类型
putPolicy.put("mimeLimit", "image/jpeg;image/png");
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, null, expireSeconds, putPolicy);
System.out.println(upToken); 
  • 自定义上传返回结果

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
String key = "file key";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
putPolicy.put("returnBody","{\"url\":\"$(key)\",\"fileSize\":\"$(fsize)\",\"mimeType\":\"$(mimeType)\",\"fileName\",\"$(fname)\",\"fileExt\":\"$(ext)\"}");
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, key, expireSeconds, putPolicy);
System.out.println(upToken);  
  • 自定义回调

添加自定义回调配置后,客户端上传到七牛后,七牛后调用自定义回调地址的接口获取返回结果,然后将返回结果返回到客户端,可以通过该方式对上传信息进行记录。

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
String key = "file key";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
putPolicy.put("callbackUrl","http://api.mldong.com/xxxx/handleCallback");
putPolicy.put("callbackBodyType","application/json");
putPolicy.put("callbackBody","{\"url\":\"$(key)\",\"fileSize\":\"$(fsize)\",\"mimeType\":\"$(mimeType)\",\"fileName\",\"$(fname)\",\"fileExt\":\"$(ext)\"}");
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, key, expireSeconds, putPolicy);
System.out.println(upToken); 
  • 综合调用

String accessKey = "access key";
String secretKey = "secret key";
String bucket = "bucket name";
Auth auth = Auth.create(accessKey, secretKey);
StringMap putPolicy = new StringMap();
// 自定义资源名支持魔法变量和自定义变量
putPolicy.put("saveKey", "biz_type/2020/06/13/uuid$(ext)");
// 限定上传文件大小最小值
putPolicy.put("fsizeMin", 0);
// 限定上传文件大小最大值
putPolicy.put("fsizeLimit", 1024 * 1024 * 10);
// 限制文件类型
putPolicy.put("mimeLimit", "image/jpeg;image/png");
// 返回结果定义
putPolicy.put("returnBody","{\"url\":\"$(key)\",\"fileSize\":\"$(fsize)\",\"mimeType\":\"$(mimeType)\",\"fileName\",\"$(fname)\",\"fileExt\":\"$(ext)\"}");
// 回调通知定义
putPolicy.put("callbackUrl","http://api.mldong.com/xxxx/handleCallback");
putPolicy.put("callbackBodyType","application/json");
putPolicy.put("callbackBody","{\"url\":\"$(key)\",\"fileSize\":\"$(fsize)\",\"mimeType\":\"$(mimeType)\",\"fileName\",\"$(fname)\",\"fileExt\":\"$(ext)\"}");
long expireSeconds = 3600;
String upToken = auth.uploadToken(bucket, null, expireSeconds, putPolicy);
System.out.println(upToken);  

开始编码

上传模块相关表设计好后,就可以生成CURD代码了,代码生成完成再对上传文件记录模块新增创建上传凭证及处理上传回调的接口。

目录结构

├── mldong-admin  管理端接口
	├── src/main/java
		├──	com.mldong.modules.sys
			├── controller
				└── SysUploadRecordController.java
			├── dto
				└── SysUploadBizTypeParam.java
            ├── service
            	├── impl
            		└──	SysUploadRecordImpl.java
				└── SysUploadRecordService.java
├── mldong-common  工具类及通用代码
	├── src/main/java
		├──	com.mldong.common
			├── upload
				├── impl
					└──	QiniuUploadImpl.java
				├── model
					├──	UploadResult.java
					├──	UploadTokenParam.java
					└── UploadTokenVo.java
                ├── CommonUpload.java
                └── UploadMimeType.java
├── mldong-generator  代码生成器

核心文件说明:

  • mldong-common/src/main/java/com/mldong/common/upload/CommonUpload.java

通用上传接口定义

package com.mldong.common.upload;

import com.mldong.common.upload.model.UploadResult;
import com.mldong.common.upload.model.UploadTokenParam;
import com.mldong.common.upload.model.UploadTokenVo;
/**
 * 通过上传接口定义
 * @author mldong
 *
 */
public interface CommonUpload {
	/**
	 * 回调请求类型
	 */
	public final static String CALLBACK_BODY_TYPE = "application/json";
	/**
	 * 回调请求时会在请求
	 */
	public final static String CALLBACK_AUTH_HEADER = "Authorization";
	/**
	 * 创建上传凭证
	 * @return
	 */
	public UploadTokenVo createUploadToken(UploadTokenParam param);
	/**
	 * 回调处理
	 * @param callbackAuthHeader
	 * @param callbackUrl
	 * @param callbackBody
	 * @return
	 */
	public UploadResult handleCallback(String callbackAuthHeader, String callbackUrl, String callbackBody);
}
  • mldong-common/src/main/java/com/mldong/common/upload/impl/QiniuUploadImpl.java

通用上传七牛云存储实现类

package com.mldong.common.upload.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.mldong.common.base.constant.GlobalErrEnum;
import com.mldong.common.config.GlobalProperties;
import com.mldong.common.exception.BizException;
import com.mldong.common.tool.DateTool;
import com.mldong.common.tool.JsonTool;
import com.mldong.common.tool.StringTool;
import com.mldong.common.upload.CommonUpload;
import com.mldong.common.upload.UploadMimeType;
import com.mldong.common.upload.model.UploadResult;
import com.mldong.common.upload.model.UploadTokenParam;
import com.mldong.common.upload.model.UploadTokenVo;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
/**
 * 通过上传七牛实现
 * @author mldong
 *
 */
@Component
public class QiniuUploadImpl implements CommonUpload{
	@Autowired
	private GlobalProperties globalProperties;
	@Override
	public UploadTokenVo createUploadToken(UploadTokenParam param) {
		String accessKey = globalProperties.getQiniuAccessKey();
		String secretKey = globalProperties.getQiniuSecretKey();
		String bucket = globalProperties.getQiniuBucket();
		Auth auth = Auth.create(accessKey, secretKey);
		StringMap putPolicy = new StringMap();
		// 自定义资源名支持魔法变量和自定义变量
		String bizType = param.getBizType();
		String dateString = DateTool.dateToString(new Date(), "yyyyMM");
		String uuid = UUID.randomUUID().toString();
		putPolicy.put("saveKey", String.format("%s/%s/%s$(ext)", bizType,dateString,uuid));
		// 限定上传文件大小最小值
		putPolicy.put("fsizeMin", param.getFileSizeMin());
		// 限定上传文件大小最大值
		if(param.getFileSizeMax()>0) {
			putPolicy.put("fsizeLimit", param.getFileSizeMax());
		}
		// 限制文件类型
		String mimeType = UploadMimeType.getMimeType(param.getFileExt(), ";");
		if(StringTool.isNotEmpty(mimeType)) {
			putPolicy.put("mimeLimit", mimeType);
		}
		
		// 返回结果定义
		Map<String,String> result = new HashMap<String, String>();
		result.put("baseUrl","$(x:baseUrl)");
		result.put("bizId","${x:bizId}");
		result.put("bizType","$(x:bizType)");
		result.put("fileExt","$(ext)");
		result.put("fileName","$(fname)");
		result.put("fileSize","$(fsize)");
		result.put("mimeType","$(mimeType)");
		result.put("url","$(key)");
		String body = JsonTool.toJson(result);
		// 设置回调
		if(StringTool.isNotEmpty(param.getCallbackUrl())) {
			putPolicy.put("callbackUrl", param.getCallbackUrl());
			putPolicy.put("callbackBody", body);
			putPolicy.put("callbackBodyType", CALLBACK_BODY_TYPE);
		}
		putPolicy.put("returnBody", body);
		long expireSeconds = 3600;
		String uploadToken = auth.uploadToken(bucket, null, expireSeconds, putPolicy);
		return new UploadTokenVo(UUID.randomUUID().toString(), uploadToken, expireSeconds, param);
	}

	@Override
	public UploadResult handleCallback(String callbackAuthHeader,
			String callbackUrl, String callbackBody) {
		String accessKey = globalProperties.getQiniuAccessKey();
		String secretKey = globalProperties.getQiniuSecretKey();
		Auth auth = Auth.create(accessKey, secretKey);
		boolean validCallback = auth.isValidCallback(callbackAuthHeader, callbackUrl, callbackBody.getBytes(), CALLBACK_BODY_TYPE);
		if(validCallback) {
			return JsonTool.jsonToBean(callbackBody, UploadResult.class);
		}
		throw new BizException(GlobalErrEnum.GL99990100);
	}
}
  • mldong-common/src/main/java/com/mldong/common/upload/UploadTokenParam.java

创建上传凭证入参实体

package com.mldong.common.upload.model;
/**
 * 创建上传凭证入参实体
 * @author mldong
 *
 */
public class UploadTokenParam {
	/**
	 * 业务类型
	 */
	private String bizType;
	/**
	 * 限定上传文件大小最小值,单位`byte`。(0为不限制)
	 */
	private long fileSizeMin;
	/**
	 * 限定上传文件大小最大值,单位`byte`。(0为不限制)
	 */
	private long fileSizeMax;
	/**
	 * 限定用户上传后辍(多个逗号分割)
	 */
	private String fileExt;
	/**
	 * 访问地址前辍
	 */
	private String baseUrl;
	/**
	 * 回调地址
	 */
	private String callbackUrl;
	/**
	 * 命名策略
	 */
	private String namingStrategy;
	/**
	 * 是否记录
	 */
	private boolean isRecord;
	// 省略 get/set
	
}

  • mldong-common/src/main/java/com/mldong/common/upload/UploadTokenVo.java

创建上传凭证返回实体

package com.mldong.common.upload.model;

import java.io.Serializable;

import io.swagger.annotations.ApiModelProperty;
/**
 * 创建上传凭证返回实体
 * @author mldong
 *
 */
public class UploadTokenVo implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = -8862086775224787600L;
	@ApiModelProperty(value="业务id")
	private String bizId;
	@ApiModelProperty(value="上传凭证")
	private String uploadToken;
	@ApiModelProperty(value="凭证过期时长(s)")
	private long expireSeconds;
	@ApiModelProperty(value="上传配置")
	private UploadTokenParam uploadConfig;
	
	public UploadTokenVo(String bizId, String uploadToken, long expireSeconds,
			UploadTokenParam uploadConfig) {
		super();
		this.bizId = bizId;
		this.uploadToken = uploadToken;
		this.expireSeconds = expireSeconds;
		this.uploadConfig = uploadConfig;
	}
	// 省略 get/set
}
  • mldong-common/src/main/java/com/mldong/common/upload/UploadResult.java

上传结果实体

package com.mldong.common.upload.model;
/**
 * 上传结果实体
 * @author mldong
 *
 */
public class UploadResult {
	/**
	 * 业务id
	 */
	private String bizId;
	/**
	 * 业务类型
	 */
	private String bizType;
	/**
	 * 基础路径
	 */
	private String baseUrl;
	/**
	 * 文件保存的资源路径
	 */
	private String url;
	/**
	 * 文件大小
	 */
	private Long fileSize;
	/**
	 * 媒体类型
	 */
	private String mimeType;
	/**
	 * 文件名
	 */
	private String fileName;
	/**
	 * 文件后辍
	 */
	private String fileExt;
	
	// 省略 get/set
	
}
  • mldong-admin/src/main/java/com/mldong/modules/sys/dto/SysUploadBizTypeParam.java

上传业务类型实体,创建凭证

package com.mldong.modules.sys.dto;

import io.swagger.annotations.ApiModelProperty;

import javax.validation.constraints.NotBlank;

public class SysUploadBizTypeParam {
	@ApiModelProperty(value="业务类型")
	@NotBlank(message="业务类型不能为空")
	private String bizType;

	public String getBizType() {
		return bizType;
	}

	public void setBizType(String bizType) {
		this.bizType = bizType;
	}
	
}
  • mldong-admin/src/main/java/com/mldong/modules/sys/service/SysUploadRecordService.java

上传记录业务接口定义,代码片段

/**
	 * 创建上传凭证
	 * @param param
	 * @return
	 */
public UploadTokenVo createUploadToken(SysUploadBizTypeParam param);
/**
	 * 回调处理
	 * @param callbackAuthHeader 请求头
	 * @param callbackBody 请求正文
	 * @return
	 */
public UploadResult handleCallback(String callbackAuthHeader, String callbackBody);
  • mldong-admin/src/main/java/com/mldong/modules/sys/service/impl/SysUploadRecordServiceImpl.java

上传记录业务接口实现,代码片段

@Override
public UploadTokenVo createUploadToken(SysUploadBizTypeParam param) {
    SysUploadConfig q = new SysUploadConfig();
    q.setBizType(param.getBizType());
    SysUploadConfig uploadConfig = sysUploadConfigMapper.selectOne(q);
    if(null == uploadConfig){
        // 文件上传配置不存在
        throw new BizException(GlobalErrEnum.GL99990008);
    }
    UploadTokenParam tokenParam = new UploadTokenParam();
    BeanUtils.copyProperties(uploadConfig, tokenParam);
    tokenParam.setRecord(YesNoEnum.YES.equals(uploadConfig.getIsRecord()));
    return commonUpload.createUploadToken(tokenParam);
}
@Override
public UploadResult handleCallback(String callbackAuthHeader,
                                   String callbackBody) {
    LOGGER.debug("【七牛上传回调-callbackAuthHeader】:{}",callbackAuthHeader);
    LOGGER.debug("【七牛上传回调-callbackBody】:{}",callbackBody);
    Map<String,Object> map = JsonTool.jsonToMap(callbackBody);
    Object bizType = map.get("bizType");
    if(bizType == null) {
        // 文件上传配置不存在
        throw new BizException(GlobalErrEnum.GL99990008);
    }
    SysUploadConfig q = new SysUploadConfig();
    q.setBizType(bizType.toString());
    SysUploadConfig uploadConfig = sysUploadConfigMapper.selectOne(q);
    if(null == uploadConfig){
        // 文件上传配置不存在
        throw new BizException(GlobalErrEnum.GL99990008);
    }
    Date now = new Date();
    UploadResult result = commonUpload.handleCallback(callbackAuthHeader, uploadConfig.getCallbackUrl(), callbackBody);
    if(YesNoEnum.YES.equals(uploadConfig.getIsRecord())){
        SysUploadRecord record = new SysUploadRecord();
        record.setBizId(result.getBizId());
        record.setBizType(result.getBizType());
        record.setCreateTime(now);
        record.setFileExt(result.getFileExt());
        record.setFileName(result.getFileName());
        record.setFileSize(result.getFileSize()==null?0L:result.getFileSize());
        record.setIsDeleted(YesNoEnum.NO);
        record.setMimeType(result.getMimeType());
        record.setUpdateTime(now);
        record.setUrl(result.getUrl());
        sysUploadRecordMapper.insertSelective(record);
    }
    return result;
}
  • mldong-admin/src/main/java/com/mldong/modules/sys/controller/SysUploadController.java

上传记录控制层,代码片段

@PostMapping("createUploadToken")
	@ApiOperation(value="创建上传凭证", notes="创建上传凭证",authorizations={
		@Authorization(value="创建上传凭证",scopes={
	    	@AuthorizationScope(description="创建上传凭证",scope="sys:upload:createUploadToken")
	    })
	})
	public CommonResult<UploadTokenVo> createUploadToken(@RequestBody SysUploadBizTypeParam param) {
		return CommonResult.success("创建上传凭证成功", sysUploadRecordService.createUploadToken(param));
	}
	@PostMapping("handleCallback")
	@ApiOperation(value="上传回调处理", notes="上传回调处理")
	@AuthIgnore
	public CommonResult<UploadResult> handleCallback(HttpServletRequest request) throws IOException {
		String callbackAuthHeader = request.getHeader(CommonUpload.CALLBACK_AUTH_HEADER);
		String callbackBody = IOUtils.toString(request.getInputStream(),"UTF-8");
		// 使用 HttpServletRequest request参数后,全局日志没办法 拿到body,需要再这里重新setBody
		LoggerModel logger = RequestHolder.getLoggerModel();
		if(null!=logger) {
			logger.setBody(callbackBody);
		}
		return CommonResult.success("上传回调处理成功", sysUploadRecordService.handleCallback(callbackAuthHeader, callbackBody));
	}
  • mldong-common/src/main/java/com/mldong/common/config/GlobalProperties.java

新增七牛配置(其实也可以在sys_upload_config上定义,如果定义不存在则使用全局的,现在只实现了全局定义。)

/**
	 * 七牛云ak
	 */
	private String qiniuAccessKey = "";
	/**
	 * 七牛云sk
	 */
	private String qiniuSecretKey = "";
	/**
	 * 七牛云空间名称
	 */
	private String qiniuBucket = "";
  • mldong-admin/src/main/resources/application-xxx.yml

配置文件新增配置项

# 系统全局配置
g.qiniuAccessKey: xxx
g.qiniuSecretKey: xxx
g.qiniuBucket: xxx

运行效果

  • 创建凭证

    • 请求地址{{base_api}}sys/uploadRecord/createUploadToken
    • 入参
    {
    	"bizType": "avatar"
    }
    
    • 出参
    {
      "code": 0,
      "msg": "创建上传凭证成功",
      "data": {
        "bizId": "7f07a3cc-77ba-47b9-af74-88b2631f2bc4",
        "uploadToken": "WKLR3_xgRzQXYdA2dBlRZdjkXQ6EU8vHkE……",
        "expireSeconds": 3600,
        "uploadConfig": {
          "bizType": "avatar",
          "fileSizeMin": 0,
          "fileSizeMax": 2097152,
          "fileExt": ".png,.jpg,.jpeg",
          "baseUrl": "http://qiniu.mldong.com/",
          "callbackUrl": "http://api.mldong.com/sys/uploadRecord/handleCallback",
          "namingStrategy": "default",
          "record": true
        }
      }
    }
    
  • 七牛回调接口(由七牛服务器调用)

    • 请求地址{{base_api}}sys/uploadRecord/handleCallback
    • 入参
    {
    	"bizId": "7f07a3cc-77ba-47b9-af74-88b2631f2bc4",
    	"bizType": "avatar",
    	"baseUrl": "http://qiniu.mldong.com/",
    	"url": "avatar/202006/50d0f9e5-fa60-4551-a0d9-714923575637.png",
    	"fileSize": "497",
    	"mimeType": "image/png",
    	"fileName": "qrcode.png",
    	"fileExt": ".png"
    }
    
    • 出参
    {
        "code": 0,
        "data": {
            "baseUrl": "http://qiniu.mldong.com/",
            "bizId": "7f07a3cc-77ba-47b9-af74-88b2631f2bc4",
            "bizType": "avatar",
            "fileExt": ".png",
            "fileName": "qrcode.png",
            "fileSize": "497",
            "mimeType": "image/png",
            "url": "avatar/202006/50d0f9e5-fa60-4551-a0d9-714923575637.png"
        },
        "msg": "上传回调处理成功"
    }
    
  • 客户端调用说明(postman),这里不截图

    Body->form-data

    • 接口地址 https://up-z2.qiniup.com (自己根据区域自行选择,可以考虑创建token的时候返回)
    • 入参
    属性 描述
    x:bizType 自定义参数,业务类型
    x:bizId 自定义参数,业务id,创建凭证时返回
    x:baseUrl 自定义参数,基础地址,创建凭证时返回
    token 上传凭证,创建凭证时返回
    file 要上传的文件
    • 出参(和回调拉如期返回的内容一致)

      • 正常样例
      {
          "code": 0,
          "data": {
              "baseUrl": "http://qiniu.mldong.com/",
              "bizId": "7f07a3cc-77ba-47b9-af74-88b2631f2bc4",
              "bizType": "avatar",
              "fileExt": ".png",
              "fileName": "qrcode.png",
              "fileSize": "497",
              "mimeType": "image/png",
              "url": "avatar/202006/50d0f9e5-fa60-4551-a0d9-714923575637.png"
          },
          "msg": "上传回调处理成功"
      }
      
      • 错误样例
      {
          "error": "expired token"
      }
      

小结

本文上传模块采用的是七牛云存储的客户端直传的方式,核心接口为创建上传凭证处理上传回调。因为处理回调需要有公网的访问地址,所以大家在选择该方案的时候需要准备好公网环境。

项目源码地址

  • 后端

gitee.com/mldong/mldo…

  • 前端

gitee.com/mldong/mldo…

相关文章

打造一款适合自己的快速开发框架-先导篇

打造一款适合自己的快速开发框架-后端脚手架搭建

打造一款适合自己的快速开发框架-集成mapper

打造一款适合自己的快速开发框架-集成swaggerui和knife4j

打造一款适合自己的快速开发框架-通用类封装之统一结果返回、统一异常处理

打造一款适合自己的快速开发框架-业务错误码规范及实践

打造一款适合自己的快速开发框架-框架分层及CURD样例

打造一款适合自己的快速开发框架-mapper逻辑删除及枚举类型规范

打造一款适合自己的快速开发框架-数据校验之Hibernate Validator

打造一款适合自己的快速开发框架-代码生成器原理及实现

打造一款适合自己的快速开发框架-通用查询设计与实现

打造一款适合自己的快速开发框架-基于rbac的权限管理

打造一款适合自己的快速开发框架-登录与权限拦截

打造一款适合自己的快速开发框架-http请求日志全局处理