Ali OSS 服务端签名并设置回调,客户端上传文件
一.最近做阿里云oss文件上传开发,一点收获分享给大家,帮助大家绕过一些坑。关于阿里云oss服务的介绍,我这里不做赘述了,可以查看阿里云OSS开发api文档。
在这里我主要介绍下,文件上传流程比较复杂的服务器设置回调功能。整个流程图是这样的:
大家可以参考阿里api文档介绍的工作流程说明:https://help.aliyun.com/document_detail/31927.html?spm=5176.doc31988.6.633.OTY557
二. 客户端请求policy,签名,key以及回调地址等
1.首先定义我们的业务类型biz_type,主要是将文件按业务类型目录存储。这里我要着重强调一下,阿里oss回调请求的自定义参数:
- ${x:biz_type},不要使用驼峰法命名,在开发的工程中,发现驼峰法命名bizType 取不到值。建议需要命名时 用"_"取代。
2.accessId ,accesskey 定义在properties文件中,这些定值从文件中读取,这里我要说明下:定义的end 和 endPoint
endpoint = http://oss-cn-shanghai.aliyuncs.com
end = oss-cn-shanghai.aliyuncs.com
bucket 是定义的桶名称就类似于文件目录名称。
2. callbackurl 定义自己的回调方法
3.签名,失效时间,policy,这些是根据阿里云官网提供的案例代码写的。
4.在定义callback字符串时,遇到一个大坑,这也是我写整篇文章的原因。
定义callback 时,阿里支持两种方式,一个是callbackBody字符串直接拼接 用&符连接。
这里我使用的是第二种方式,callbackBody 采用json字符串。但是这跟以往的字符串不一样,中间有阿里自带的字符bucket,object,size等以及自定义的字符,ali oss 需要解析自定义字符,这里可以理解成
变量的一个占位符,那么json串是如何拼接一个占位符变量呢???带着这个问题,我就一遍一遍的拼串,尝试了大概有两小时,看了官网的一篇错误排查文章:https://yq.aliyun.com/articles/68863/
定义的回调字符串是这样的:
{
"callbackUrl" : "http://abc.com/test.php",
"callbackHost" : "oss-cn-hangzhou.aliyuncs.com",
"callbackBody" : "{\"bucket\":${mimeType}, \"object\":${object},\"size\":${size},\"mimeType\":${mimeType},\"my_var\":${x:my_var}}",
"callbackBodyType" : "application/json"
}
顿时眼睛亮了,callbackBody 定义的原来是一个字符串的jsonobject,一直以为需要定义一个占位符类型的jsonobject对象。
然后继续拼串,
定义的callbackBody就是这样:
"{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type}}"
最终将 resMap 转成json 字符返回。
- public String reqOssServer() throws ServletException, IOException {
- String[] bizArray = new String[]{"","user-dir/","advise-dir/"};
- String biz_type = getRequest().getParameter("biz_type");
- String sysCurrtime = System.currentTimeMillis()+"";
- //15位随机码
- String randStr = sysCurrtime.substring(5,sysCurrtime.length())+CommonUtil.getRamdon(15);
- //目录
- String dir = bizArray[Integer.valueOf(biz_type)];
- PropertiesUtil pro = new PropertiesUtil("oss.properties");
- String end = pro.readValue("end");
- String endpoint = pro.readValue("endpoint");
- String accessId = pro.readValue("accessKeyId");
- String accessKey = pro.readValue("accessKeySecret");
- String bucket = pro.readValue("bucketName");
- String host = "https://" + bucket + "." + end;
- //oss 回调地址
- String callbackurl = Cont.TEST_DOMAIN_NAME+"/osscallback";
- OSSClient client = new OSSClient(endpoint, accessId, accessKey);
- try {
- long expireTime = 3000;
- long expireEndTime = System.currentTimeMillis() + expireTime * 100;
- Date expiration = new Date(expireEndTime);
- PolicyConditions policyConds = new PolicyConditions();
- policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
- policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
- String postPolicy = client.generatePostPolicy(expiration, policyConds);
- byte[] binaryData = postPolicy.getBytes("utf-8");
- String encodedPolicy = BinaryUtil.toBase64String(binaryData);
- String postSignature = client.calculatePostSignature(postPolicy);
- Map<String, String> respMap = new LinkedHashMap<String, String>();
- URL url = client.generatePresignedUrl(bucket, accessKey, expiration);
- respMap.put("accessid", accessId);
- respMap.put("policy", encodedPolicy);
- respMap.put("signature", postSignature);
- //respMap.put("expire", formatISO8601Date(expiration));
- respMap.put("dir", dir);
- respMap.put("host", host);
- respMap.put("expire", String.valueOf(expireEndTime / 1000));
- respMap.put("url", url.toString());
- respMap.put("key", randStr);
- //bucket=${bucket}&object=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&my_var=${x:my_var}
- /* String callback = "{\"callbackUrl\":\"" + callbackurl + "\",\"callbackBody\":\"filename=${object}&size=${size}&mimeType=${mimeType}&"
- + "orderid=" + orderid + "&calbackright=" + calbackright + "\",\"callbackBodyType\":\"application/x-www-form-urlencoded\"}";*/
- //先传入一个固定值测试
- // String callback = "{\"callbackUrl\":\"" + callbackurl + "\",\"callbackBody\":\"filename=${object}&size=${size}&mimeType=${mimeType}&orderid=123\",\"callbackBodyType\":\"application/x-www-form-urlencoded\"}";
- //String callback = "{\"callbackUrl\":\""+callbackurl+"\",\"callbackBody\":\"{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid}}\",\"callbackBodyType\":\"application/json\"}";
- String callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type}}";
- if("1".equals(biz_type)){//用户头像,banner修改
- callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type},\'portrait\':${x:portrait},\'banner\':${x:banner}}";
- }else if("2".equals(biz_type)){//投诉建议
- callbackbody = "{\'bucket\':${bucket}, \'object\':${object},\'size\':${size},\'mimeType\':${mimeType},\'uid\':${x:uid},\'biz_type\':${x:biz_type},\'path\':${x:path},\'guideid\':${x:guideid}}";
- }
- String callback = "{\"callbackUrl\":\""+callbackurl+"\",\"callbackBody\":\""+callbackbody+"\",\"callbackBodyType\":\"application/json\"}";
- byte[] bytes = Base64.encodeBase64(callback.getBytes("UTF-8"));
- respMap.put("callback", new String(bytes));
- //respMap.put("callback_str", callback);
- JSONObject ja1 = JSONObject.fromObject(respMap);
- System.out.println("=====respMap:===== "+ja1.toString());
- getResponse().setHeader("Access-Control-Allow-Origin", "*");
- getResponse().setHeader("Access-Control-Allow-Methods", "GET, POST");
- setJsonString(AppJSON.succReq("请求成功", ja1.toString()));
- } catch (Exception e) {
- Assert.fail(e.getMessage());
- }
- return "ajax";
- }
三. 回调方法处理业务逻辑
该回调方法是一个servlet,需要在web.xml中配置 访问的路径地址。在servlet中,主要进行业务处理,这里就不在贴出我的业务处理了,大家根据自己的实际需要进行编码。
- package com.zd.servlet;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.net.URI;
- import java.net.URL;
- import java.security.KeyFactory;
- import java.security.PublicKey;
- import java.security.spec.X509EncodedKeySpec;
- import java.text.ParseException;
- import java.util.Date;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import net.sf.json.JSONObject;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.impl.client.DefaultHttpClient;
- import com.aliyun.oss.common.utils.BinaryUtil;
- import com.zd.aliyun.oss.HttpMethod;
- import com.zd.aliyun.oss.OSSClient;
- import com.zd.aliyun.oss.common.utils.DateUtil;
- import com.zd.aliyun.oss.model.GeneratePresignedUrlRequest;
- import com.zd.util.Cont;
- import com.zd.util.FileUtil;
- import com.zd.util.StringUtil;
- import com.zd.util.codeBuild.PropertiesUtil;
- @SuppressWarnings("deprecation")
- @WebServlet(asyncSupported = true)
- public class OssCallBackService extends HttpServlet {
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- this.doPost(req, resp);
- response(req, resp, "input get ", 200);
- }
- @SuppressWarnings({ "finally" })
- public String executeGet(String url) {
- BufferedReader in = null;
- String content = null;
- try {
- // 定义HttpClient
- @SuppressWarnings("resource")
- DefaultHttpClient client = new DefaultHttpClient();
- // 实例化HTTP方法
- HttpGet request = new HttpGet();
- request.setURI(new URI(url));
- HttpResponse response = client.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();
- } catch (Exception e) {
- } finally {
- if (in != null) {
- try {
- in.close();// 最后要关闭BufferedReader
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return content;
- }
- }
- public String GetPostBody(InputStream is, int contentLen) {
- if (contentLen > 0) {
- int readLen = 0;
- int readLengthThisTime = 0;
- byte[] message = new byte[contentLen];
- try {
- while (readLen != contentLen) {
- readLengthThisTime = is.read(message, readLen, contentLen - readLen);
- if (readLengthThisTime == -1) {// Should not happen.
- break;
- }
- readLen += readLengthThisTime;
- }
- return new String(message);
- } catch (IOException e) {
- }
- }
- return "";
- }
- //oss回调请求
- protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws NumberFormatException, IOException
- {
- boolean ret = false;
- String autorizationInput = new String(request.getHeader("Authorization"));
- String pubKeyInput = request.getHeader("x-oss-pub-key-url");
- byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
- byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
- String pubKeyAddr = new String(pubKey);
- if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/"))
- {
- System.out.println("pub key addr must be oss addrss");
- return false;
- }
- String retString = executeGet(pubKeyAddr);
- retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
- retString = retString.replace("-----END PUBLIC KEY-----", "");
- String queryString = request.getQueryString();
- String uri = request.getRequestURI();
- String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
- String authStr = decodeUri;
- if (queryString != null && !queryString.equals("")) {
- authStr += "?" + queryString;
- }
- authStr += "\n" + ossCallbackBody;
- ret = doCheck(authStr, authorization, retString);
- return ret;
- }
- protected void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //HttpServletRequest
- String ossCallbackBody = GetPostBody(request.getInputStream(), Integer.parseInt((request).getHeader("content-length")));
- System.out.println("ossCallbackBody == "+ossCallbackBody);
- boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
- System.out.println("verify result:" + ret);
- // String aaa ="{\"bucket\":\"picture-zoomdu\",\"object\":\"2017072131713.jpg\",\"size\":\"12345\","gid":"86","uid":"121","type":"1"}
- JSONObject obj = JSONObject.fromObject(ossCallbackBody);
- PropertiesUtil pro = new PropertiesUtil("oss.properties");
- String endpoint = pro.readValue("endpoint");
- String accessKeyId = pro.readValue("accessKeyId");
- String accessKeySecret = pro.readValue("accessKeySecret");
- String bucketName = obj.get("bucket").toString();
- String bucketkey = obj.get("object").toString();
- OSSClient ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
- Date expirations = null;
- try {
- expirations = DateUtil.parseRfc822Date("Wed, 18 Mar 2019 14:20:00 GMT");
- } catch (ParseException e) {
- e.printStackTrace();
- }
- GeneratePresignedUrlRequest requestGenerate = new GeneratePresignedUrlRequest(bucketName, bucketkey, HttpMethod.GET);
- requestGenerate.setExpiration(expirations);
- URL signedUrl = ossClient.generatePresignedUrl(requestGenerate);
- String path =signedUrl.toString();
- if(signedUrl.toString().indexOf("?")>-1){
- path = path.substring(0,signedUrl.toString().indexOf("?"));
- }
- // 图片压缩配置的字符串
- path += "?x-oss-process=style/zd_list_image";
- String ossurl = "{\"ossurl\":\""+signedUrl.toString()+"\"}";
- if (ret){//签名验证通过,业务处理
- //客户端 在回调中需要 传入的字段
- response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK);
- //业务处理..... 此处省略
- if(obj.containsKey("uid") && StringUtils.isNotEmpty(obj.get("uid")+"")){
- String uid = obj.get("uid").toString();
- //biz_type : 1:用户头像、banner ,2:投诉建议图片上传 }
- }
- else{
- response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);
- }
- }
- public static 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) {
- e.printStackTrace();
- }
- return false;
- }
- private void response(HttpServletRequest request, HttpServletResponse response, String results, int status) throws IOException {
- String callbackFunName = request.getParameter("callback");
- System.out.println("callbackFunName:"+callbackFunName);
- response.addHeader("Content-Length", String.valueOf(results.length()));
- if (callbackFunName == null || callbackFunName.equalsIgnoreCase(""))
- response.getWriter().println(results);
- else
- response.getWriter().println(callbackFunName + "( " + results + " )");
- response.setStatus(status);
- response.flushBuffer();
- }
- }
四.关于oss 上传,并设置回调就介绍到这里,本篇文章没有介绍,oss服务配置,主要是讲技术坑。如有什么问题,请留言联系我,很乐意解决大家的问题。
另外郑重说明,本篇文章是本人原创,如需转载 请标注转载原文地址。
Ali OSS 服务端签名并设置回调,客户端上传文件的更多相关文章
- 阿里云OSS 服务端签名后直传之分片上传(结合element-ui的upload组件)
分片上传(结合element-ui的upload组件实现自定义上传) async uploadFree(content){ let data = await this.getOssToken(); / ...
- Ali OSS服务端签名直传并设置上传回调
服务端签名直传并设置上传回调 背景 请参考 Web端直传实践 里的背景介绍. 当采用服务端签名后直传方案后,问题来了,用户上传数据后,很多场景下,应用服务器都要知道用户上传了哪些文件,文件名字,甚至如 ...
- PHP服务端如何通过程序将图上传到指定的图片服务器与图片服务器的优化方案
一:PHP服务端如何通过程序将图上传到指定的图片服务器与图片服务器的优化方案 (1) php服务器把图片处理成缩率图或指定大小的图片在通过PHP程序代码 操作FTP 上传到图片服务器. 二:图片服务器 ...
- Wince 6.0适用 .NET 使用HttpRequest的Post上传文件,服务端的Web API接收Post上传上来的文件 代码
//调用的示例 private string fileName = "InStorageData.csv"; string filePath = parentPath + Comm ...
- ecstore-ftp设置,不能上传文件
某些主机居然不能上传,ftp改成127.0.0.1即可 ftp地址改成127.0.0.1即可...
- Tomcat设置是否可以上传文件到服务器
今天,我做的一个点菜项目要求做一个添加菜品,把菜品的路径保存进数据库,然后将菜品的图片保存进tomcat相应的目录中. 一开始,我在客户端写的代码是直接向tomcat的目录写文件,但是会出现403错误 ...
- MVC文件上传08-使用客户端jQuery-File-Upload插件和服务端Backload组件让每个用户有专属文件夹
当需要为每个用户建立一个专属上传文件夹的时候,可以在提交文件的视图中添加一个隐藏域,并设置name="objectContext". 相关兄弟篇: MVC文件上传01-使用jque ...
- mui plus.uploader.createUpload 上传文件服务端获取文件名中文乱码问题
客户端上传文件需要做一次url编码:encodeURIComponent(fileName) 服务端:URL解码 var fileName = HttpUtility.UrlDecode(hfc.Fi ...
- react native android 上传文件,Nodejs服务端获取上传的文件
React Native端 使用react-native-image-picker 做出选择图片的操作,选择完成后,直接将图片Post至服务器,保存在服务器的某个地方(保存图片的路径需要公开显示),并 ...
随机推荐
- Hadoop(十二)MapReduce概述
前言 前面以前把关于HDFS集群的所有知识给讲解完了,接下来给大家分享的是MapReduce这个Hadoop的并行计算框架. 一.背景 1)爆炸性增长的Web规模数据量 2)超大的计算量/计算复杂度 ...
- 使用bitset实现毫秒级查询(二)
在上一篇中我们了解了bitset索引的基本用法,本篇开始学习bitset索引更新及一些复杂查询. 1.bitset索引更新 因为我们的数据是在系统启动时全部加载进内存,所以当数据库数据发生变化时要 ...
- C++虚函数(09)
一旦基类定义了虚函数,该基类的派生类中的同名函数也自动称为虚函数. 虚函数只能是类中的一个成员函数,但不能是静态成员,关键字virtual用于类中该函数的声明中. 关键字virtual指示C++编译器 ...
- MongoDB基本命令行操作
1. 连接MongoDB: Mongodb://username:password@hostname/dbname 2. 创建数据库: use dbname:如果数据库不存在则创建数据库,否则切换到指 ...
- PHP设计模式四:适配器模式
一.什么是适配器模式 适配器模式有两种:类适配器模式和对象适配器模式.其中类适配器模式使用继承方式,而对象适配器模式使用组合方式.由于类适配器 模式包含双重继承,而PHP并不支持双重继承,所以一般都采 ...
- 关于IntelliJ IDEA删除项目
刚开始使用IDEA . 自己创建项目玩,结果发现IDEA无法删除,我也是醉了,Eclipse直接右键 -> delete -> 勾选删除源文件 就删除了,IDEA死活没有找到删除选项... ...
- CodeForces - 846F Random Query(期望)
You are given an array a consisting of n positive integers. You pick two integer numbers l and r fro ...
- Maple trees(最小覆盖圆)
Maple trees Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total S ...
- Python filter用法
class filter(object) | filter(function or None, iterable) --> filter object | | Return an iterato ...
- XMLHttpRequest2 异步 ajax
XMLHttpRequest1只是对已经存在的xhr对象细节进行规范定义, XMLHttpRequest2升级了该对象. FormData 类型可以用在xhr传输的时候,把表单序列化或者将数据以表 ...