Oss图片上传(签名生成版)
参考开源项目:macrozheng/mall
一、创建oss
1.OSS
阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。OSS可用于图片、音视频、日志等海量文件的存储。各种终端设备、Web网站程序、移动应用可以直接向OSS写入或读取数据。
2.OSS中的相关概念
- Endpoint:访问域名,通过该域名可以访问OSS服务的API,进行文件上传、下载等操作。
- Bucket:存储空间,是存储对象的容器,所有存储对象都必须隶属于某个存储空间。
- Object:对象,对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。
- AccessKey:访问密钥,指的是访问身份验证中用到的 AccessKeyId 和 AccessKeySecret。
3.OSS的相关设置
开通OSS服务
- 登录阿里云官网;
- 将鼠标移至产品标签页,单击对象存储 OSS,打开OSS 产品详情页面;
- 在OSS产品详情页,单击立即开通。
创建存储空间
- 点击网页右上角控制台按钮进入控制台
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传qfimg-
-
选择我的云产品中的对象存储OSS
-
点击左侧存储空间的加号新建存储空间
- 新建存储空间并设置读写权限为公共读
跨域资源共享(CORS)的设置
由于浏览器处于安全考虑,不允许跨域资源访问,所以我们要设置OSS的跨域资源共享。
- 选择一个存储空间,打开其权限管理
- 点击跨越设置的设置按钮
- 点击创建规则
- 进行跨域规则设置
4.服务端签名后前端直传的相关说明
流程示例图
流程介绍
- Web前端请求应用服务器,获取上传所需参数(如OSS的accessKeyId、policy、callback等参数)
- 应用服务器返回相关参数
- Web前端直接向OSS服务发起上传文件请求
- 等上传完成后OSS服务会回调应用服务器的回调接口
- 应用服务器返回响应给OSS服务
- OSS服务将应用服务器回调接口的内容返回给Web前端
二、代码编写
1.新键springboot项目
2.导入依赖
<!--oss依赖-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.5.0</version>
</dependency>
<!--Hutool工具集-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.4.0</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--lombok懒人插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3.配置yaml
aliyun:
oss:
endpoint: oss-cn-shenzhen.aliyuncs.com # oss对外服务的访问域名
accessKeyId: # 访问身份验证中用到用户标识
accessKeySecret: # 用户用于加密签名字符串和oss用来验证签名字符串的密钥
bucketName: # oss的存储空间
policy:
expire: 300 # 签名有效期(S)
maxSize: 10 # 上传文件大小(M)
callback: http://127.0.0.1:8080/aliyun/oss/callback # 文件上传成功后的回调地址
dir:
prefix: mall/images/ # 上传文件夹路径前缀
4.config配置类
-
swagger配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * Swagger API文档相关配置 * Created by macro on 2018/4/26. */ @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean //配置docket以配置Swagger具体参数 public Docket docket1() { return new Docket(DocumentationType.SWAGGER_2) .groupName("Auth(auth)") .select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口 .apis(RequestHandlerSelectors.basePackage("cn.kgc.controller")) .paths(PathSelectors.any()) .build(); } }
-
oss配置类
import com.aliyun.oss.OSSClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * OSS对象存储相关配置 * Created by macro on 2018/5/17. */ @Configuration public class OssConfig { @Value("${aliyun.oss.endpoint}") private String ALIYUN_OSS_ENDPOINT; @Value("${aliyun.oss.accessKeyId}") private String ALIYUN_OSS_ACCESSKEYID; @Value("${aliyun.oss.accessKeySecret}") private String ALIYUN_OSS_ACCESSKEYSECRET; @Bean public OSSClient ossClient(){ return new OSSClient(ALIYUN_OSS_ENDPOINT,ALIYUN_OSS_ACCESSKEYID,ALIYUN_OSS_ACCESSKEYSECRET); } }
-
跨域配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 全局跨域配置 * Created by macro on 2019/7/27. */ @Configuration public class GlobalCorsConfig { /** * 允许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //允许所有域名进行跨域调用 config.addAllowedOriginPattern("*"); //允许跨越发送cookie config.setAllowCredentials(true); //放行全部原始头信息 config.addAllowedHeader("*"); //允许所有请求方法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
5.创建dto
-
import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * 获取OSS上传文件授权返回结果 * Created by macro on 2018/5/17. */ @Data @EqualsAndHashCode(callSuper = false) public class OssPolicyResult { @ApiModelProperty("访问身份验证中用到用户标识") private String accessKeyId; @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串") private String policy; @ApiModelProperty("对policy签名后的字符串") private String signature; @ApiModelProperty("上传文件夹路径前缀") private String dir; @ApiModelProperty("oss对外服务的访问域名") private String host; @ApiModelProperty("上传成功后的回调设置") private String callback; }
-
import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * oss上传文件的回调结果 * Created by macro on 2018/5/17. */ @Data @EqualsAndHashCode(callSuper = false) public class OssCallbackResult { @ApiModelProperty("文件名称") private String filename; @ApiModelProperty("文件大小") private String size; @ApiModelProperty("文件的mimeType") private String mimeType; @ApiModelProperty("图片文件的宽") private String width; @ApiModelProperty("图片文件的高") private String height; }
-
import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; /** * oss上传成功后的回调参数 * Created by macro on 2018/5/17. */ @Data @EqualsAndHashCode(callSuper = false) public class OssCallbackParam { @ApiModelProperty("请求的回调地址") private String callbackUrl; @ApiModelProperty("回调是传入request中的参数") private String callbackBody; @ApiModelProperty("回调时传入参数的格式,比如表单提交形式") private String callbackBodyType; }
6.service层
import cn.kgc.beans.dto.OssCallbackResult;
import cn.kgc.beans.dto.OssPolicyResult;
import javax.servlet.http.HttpServletRequest;
/**
* Oss对象存储管理Service
* Created by macro on 2018/5/17.
*/
public interface OssService {
/**
* oss上传策略生成
*/
OssPolicyResult policy();
/**
* oss上传成功回调
*/
OssCallbackResult callback(HttpServletRequest request);
}
import cn.hutool.json.JSONUtil;
import cn.kgc.beans.dto.OssCallbackParam;
import cn.kgc.beans.dto.OssCallbackResult;
import cn.kgc.beans.dto.OssPolicyResult;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* oss上传管理Service实现类
* Created by macro on 2018/5/17.
*/
@Service
public class OssServiceImpl implements OssService {
private static final Logger LOGGER = LoggerFactory.getLogger(cn.kgc.service.OssServiceImpl.class);
@Value("${aliyun.oss.policy.expire}")
private int ALIYUN_OSS_EXPIRE;
@Value("${aliyun.oss.maxSize}")
private int ALIYUN_OSS_MAX_SIZE;
@Value("${aliyun.oss.callback}")
private String ALIYUN_OSS_CALLBACK;
@Value("${aliyun.oss.bucketName}")
private String ALIYUN_OSS_BUCKET_NAME;
@Value("${aliyun.oss.endpoint}")
private String ALIYUN_OSS_ENDPOINT;
@Value("${aliyun.oss.dir.prefix}")
private String ALIYUN_OSS_DIR_PREFIX;
@Autowired
private OSSClient ossClient;
/**
* 签名生成
*/
@Override
public OssPolicyResult policy() {
OssPolicyResult result = new OssPolicyResult();
// 存储目录
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
// 签名有效期
long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
Date expiration = new Date(expireEndTime);
// 文件大小
long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;
// 回调
OssCallbackParam callback = new OssCallbackParam();
callback.setCallbackUrl(ALIYUN_OSS_CALLBACK);
callback.setCallbackBody("filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
callback.setCallbackBodyType("application/x-www-form-urlencoded");
// 提交节点
String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
try {
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String policy = BinaryUtil.toBase64String(binaryData);
String signature = ossClient.calculatePostSignature(postPolicy);
String callbackData = BinaryUtil.toBase64String(JSONUtil.parse(callback).toString().getBytes("utf-8"));
// 返回结果
result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
result.setPolicy(policy);
result.setSignature(signature);
result.setDir(dir);
result.setCallback(callbackData);
result.setHost(action);
} catch (Exception e) {
LOGGER.error("签名生成失败", e);
}
return result;
}
@Override
public OssCallbackResult callback(HttpServletRequest request) {
OssCallbackResult result= new OssCallbackResult();
String filename = request.getParameter("filename");
filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/").concat(filename);
result.setFilename(filename);
result.setSize(request.getParameter("size"));
result.setMimeType(request.getParameter("mimeType"));
result.setWidth(request.getParameter("width"));
result.setHeight(request.getParameter("height"));
return result;
}
}
7.controller层
import cn.kgc.beans.dto.OssCallbackResult;
import cn.kgc.beans.dto.OssPolicyResult;
import cn.kgc.service.OssService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
/**
* Oss对象存储管理Controller
* Created by macro on 2021/1/14.
*/
@Controller
@Api(tags = "OssController", description = "Oss管理")
@RequestMapping("/aliyun/oss")
public class OssController {
@Autowired
private OssService ossService;
@ApiOperation(value = "oss上传签名生成")
@RequestMapping(value = "/policy", method = RequestMethod.GET)
@ResponseBody
public OssPolicyResult policy() {
OssPolicyResult result = ossService.policy();
return result;
}
@ApiOperation(value = "oss上传成功回调")
@RequestMapping(value = "/callback", method = RequestMethod.POST)
@ResponseBody
public OssCallbackResult callback(HttpServletRequest request) {
OssCallbackResult ossCallbackResult = ossService.callback(request);
return ossCallbackResult;
}
public static void main(String[] args) {
System.out.println(aa());
}
public static int aa(){
try{
return 5/0;
} catch(Exception e){
return 2*3;
} finally {
return 3;
}
}
}
8.前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>oss</title>
</head>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<body>
<script>
var accessKeyId = "";
var policy = "";
var signature = "";
var dir = "";
var host = "";
var callback = "";
//访问后台接口拿到签名
function uploadPhoto() {
$.ajax({
url:"http://localhost:8080/aliyun/oss/policy",
type:"get",
success: function(data) {
accessKeyId = data.accessKeyId;
policy = data.policy;
signature = data.signature;
dir = data.dir;
host = data.host;
},
error:function(data) {
alert("签名生成失败")
}
});
$("#photoFile").click();
}
/**
* 上传图片到oss服务器
*/
function upload() {
if ($("#photoFile").val() == '') {
return;
}
var formData = new FormData();
formData.append('policy', policy);
formData.append('signature', signature);
formData.append('key', dir+'/${filename}');
formData.append('ossaccessKeyId', accessKeyId);
formData.append('dir', dir);
formData.append('host', host);
formData.append('file', document.getElementById('photoFile').files[0]);
$.ajax({
url:"http://likaifeng.oss-cn-shenzhen.aliyuncs.com/",
type:"post",
data: formData,
contentType: false,
processData: false,
success: function(data) {
console.log()
var file=$("#photoFile").val()
//正则表达式获取文件名,带后缀
var filename=file.replace(/.*(\/|\\)/, "");
//正则表达式获取后缀
var fileExt=(/[.]/.exec(filename)) ? /[^.]+$/.exec(filename.toLowerCase()) : '';
$("#preview_photo").attr("src","http://likaifeng.oss-cn-shenzhen.aliyuncs.com/"+dir+"/"+filename);
},
error:function(data) {
alert("上传失败")
}
});
}
</script>
<button onclick="uploadPhoto()">选择图片</button>
<input type="file" id="photoFile" style="display: none;" onchange="upload()">
<img id="preview_photo" src="">
</body>
</html>
9.测试
上传图片前
上传图片后。成功上传
首先访问policy拿到签名
携带签名上传图片到oss服务器