OSS上传文件的三种模式
1.web端直传种模式
Web端常见的上传方法是用户在浏览器或app端上传文件到应用服务器,然后应用服务器再把文件上传到OSS,如下图所示:
特点:上传慢。先上传到应用服务器,再上传到OSS
2.服务端签名后直传
采用javaScript端向服务端发起签名请求,获取参数后直传OSS
特点:客户端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。
3.服务端签名后直传并设置上传回调
客户端向服务端发起签名请求后,直传OSS然后OSS会回调对应的服务端回调接口,OSS回调完成后,应用服务器再返回结果给客户端。
特点:用户上传一个文件到OSS后,OSS会将上传结果返回给应用服务器,然后应用服务器会给OSS响应,然后OSS会将相关响应通知给客户端的用户。
后端(Java)代码案例:
1、导入阿里云的jar
<!-- OSS -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- json序列化反序列化 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
在编写代码的时候注意导入的jar是否为此jar,本文引入的为此jar,因为阿里云的jar更新迭代的几个版本有依赖关系。
1、Controller层
@CrossOrigin
@RestController
public class OssController {
@Autowired
OssService ossService;
/**
* 获取oss系统配置
* @return
*/
@PostMapping("/oss/config/get")
@ApiOperation("获取oss系统配置")
public JsonResult<Map> getOssConfig(){
try {
return JsonResult.ok(ossService.generateOssParam());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取回调
* @param callBackHost
* @param callBackInterFace
* @param callBackBody
* @param callbackBodyType
* @return
* @throws UnsupportedEncodingException
*/
@PostMapping("/call/back/get")
@ApiOperation("获取回调")
public String generateCallBack(String callBackHost, String callBackInterFace, String callBackBody,String callbackBodyType) throws UnsupportedEncodingException {
return ossService.generateCallBack(callBackHost,callBackInterFace,callBackBody,callbackBodyType);
}
/**
* 获取签名
* @param callBack 回调信息
* @return
*/
@PostMapping("/generateSign/get")
@ApiOperation("获取签名")
public JsonResult<Map> generateSign(String callBack){
try {
return JsonResult.ok(ossService.generateSign(callBack));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* @Description OSS上传回调
* @Author
* @Param [ossCallbackBody, authorization, publicKeyUrlBase64, request, response]
* @Return com.ejsino.chat.config.domain.JSONResult
* @return
*/
@PostMapping("/ossCallBack")
@ApiOperation("OSS上传回调")
public JsonResult<String> callBack(@RequestBody String ossCallbackBody, @RequestHeader("Authorization") String authorization,
@RequestHeader("x-oss-pub-key-url") String publicKeyUrlBase64, HttpServletRequest request,
HttpServletResponse response) {
boolean isCallBack = ossService.verifyOSSCallbackRequest(authorization, publicKeyUrlBase64, ossCallbackBody, request.getQueryString(), request.getRequestURI());
if (isCallBack) {
response.setStatus(HttpServletResponse.SC_OK);
// 此处必须使用json形式返回,不然oss回调报 Response body is not valid json format.
return JsonResult.ok("success");
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
// 此处必须使用json形式返回,不然oss回调报 Response body is not valid json format.
return JsonResult.ok("error");
}
}
2、Service层
@Service
public class OssService {
private Logger log = LoggerFactory.getLogger("logger");
/**
* @Description OSS上传需要的OSS服务参数获取
* @Param []
* @Return java.util.HashMap
*/
public HashMap<String, Object> generateOssParam() throws Exception {
try {
// 获取域名
String domain = "<填写自己应用服务器地址>";
// 文件路径
String dir = "<填写oss文件路径>"; // 例如: 'mytest' 切记不要写成 '/mytest' !!!
String bucketName = "" ;
String endPoint = "" ;
String accessKeyId = "" ;
String host = "http://" + bucketName;
HashMap<String, Object> respMap = new LinkedHashMap<String, Object>();
respMap.put("accessid", accessKeyId);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("domain", domain);
return respMap;
} catch (Exception e) {
log.error("获取OSS上传参数失败:" + e);
throw new Exception("获取OSS上传参数失败!");
}
}
/**
* @Description OSS回调系统服务参数生成
* @Param [callBackHost:要OSS进行回调的服务域名(不带http),
* callBackInterFace:要进行回调的接口(oss上传结束进行请求的接口),
* callBackBody:进行回调时携带的参数(以:key=value 的形式进行携带)]
* @Return java.lang.String
*/
public String generateCallBack(String callBackHost, String callBackInterFace, String callBackBody,String callbackBodyType) throws UnsupportedEncodingException {
Map<String, Object> callbackMap = new HashMap<String, Object>();
callbackMap.put("callbackUrl", "http://" + callBackHost + callBackInterFace);
callbackMap.put("callbackHost", callBackHost);
callbackMap.put("callbackBody", callBackBody);
callbackMap.put("callBackType",callbackBodyType);
callbackMap.put("callbackBodyType", "application/x-www-form-urlencoded");
byte[] bytes = JSON.toJSONString(callbackMap).getBytes();
String callBackString = BinaryUtil.toBase64String(bytes);
System.err.println(callBackString);
return callBackString;
}
/**
* @Description 生成签名
* @Param [callBack :要进行回调的参数,传入为空即默认为不进行回调]
* @Return java.util.HashMap
*/
public HashMap<String, Object> generateSign(String callBack) throws Exception {
//获取上传oss需要的基本参数
String endPoint = "" ;
String accessKeyId = "" ;
String accessKeySecret = "" ;
String dir = "<填写oss文件路径>"; // 例如: 'mytest' 切记不要写成 '/mytest' !!!
OSSClient client = null;
try {
//开启OSS客户端
client = new OSSClient(endPoint, accessKeyId, accessKeySecret);
long expireTime = 100;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
//生成的到期时间转换位s,并转换为String
String expire = String.valueOf(expireEndTime / 1000);
PolicyConditions policyConditions = new PolicyConditions();
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
//根据到期时间生成policy
Date expiration = new Date(expireEndTime);
String postPolicy = client.generatePostPolicy(expiration, policyConditions);
//生成signature
String postSignature = client.calculatePostSignature(postPolicy);
//对policy进行UTF-8编码后转base64
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
//生成上传文件的文件名
String fileName = "mytest" + UUID.randomUUID().toString();
//封装生成好的数据进行参数返回
HashMap<String, Object> respMap = new LinkedHashMap<String, Object>();
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("expire", expire);
respMap.put("fileName", fileName);
//callBack不为空时为OSS回调web服务上传
if (callBack != null && callBack != "") {
respMap.put("callback", callBack);
}
return respMap;
} catch (Exception e) {
log.error("生成OSS上传签名失败:" + e);
throw new Exception("生成OSS上传签名失败!");
} finally {
if (client != null) {
client.shutdown();
}
}
}
/**
* @Description OSS回调请求验证
* @Param [authorizationInput, pubKeyInput, ossCallbackBody, queryString, uri]
* @Return boolean
*/
public boolean verifyOSSCallbackRequest(String authorizationInput, String pubKeyInput, String ossCallbackBody, String queryString, String uri){
boolean ret = false;
try {
//将base64编码的数据进行还原
byte[] authorization = BinaryUtil.fromBase64String(authorizationInput);
byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
String pubKeyAddr = new String(pubKey);
if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
log.error("pub key addr must be oss address");
return false;
}
//获取请求中的公钥信息
String retString = executeGet(pubKeyAddr);
retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
retString = retString.replace("-----END PUBLIC KEY-----", "");
String decodeUri = URLDecoder.decode(uri, "utf-8");
if (queryString != null && !"".equals(queryString)) {
decodeUri += "?" + queryString;
}
decodeUri += "\n" + ossCallbackBody;
ret = doCheck(decodeUri, authorization, retString);
} catch (Exception e) {
ret = false;
log.error("验证OSS请求出现异常:" + e);
}
return ret;
}
/**
* @Description 获取请求中的参数
* @Param [pubKeyUrl]
* @Return java.lang.String
*/
@SuppressWarnings({"finally"})
private String executeGet(String pubKeyUrl) throws Exception {
BufferedReader in = null;
String content = null;
try {
// 定义HttpClient
@SuppressWarnings("resource")
DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
// 实例化HTTP方法
HttpGet request = new HttpGet();
request.setURI(new URI(pubKeyUrl));
HttpResponse response = defaultHttpClient.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
content = sb.toString();
return content;
} catch (Exception e) {
log.error("解析公钥参数失败:" + e);
throw new Exception("解析公钥参数失败!");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error("关闭BufferedReader出现异常:" + e);
}
}
}
}
/**
* @Description 对请求参数进行规则校验
* @Param [content, sign, publicKey]
* @Return boolean
*/
private boolean doCheck(String content, byte[] sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
signature.initVerify(pubKey);
signature.update(content.getBytes());
boolean bverify = signature.verify(sign);
return bverify;
} catch (Exception e) {
log.error("校验出现异常:" + e);
}
return false;
}
}
至此,Java使用OSS直传就完成啦,其中 ‘<>’ 中的值按照提示更改即可,如遇到 xml 异常可以查询 OSS对象存储错误响应