OSS文件上传(页面直传)
【前言】
近段时间在写一个文件上传的小功能,,公司的老项目实际已经有上传功能,,但是经理说在上传大文件的时候失败几率很大,,于是就仔仔细细的看了下老系统的文件上传的写法,在看了其写法之后决定重新整理一下。
因为使用的是阿里的OSS文件存储服务,所以去看了阿里的官方文档 OSS文档 ,然后着手去开发一个新模式的OSS文件上传。
OSS上传文件的三种模式
1.web端直传
Web端常见的上传方法是用户在浏览器或app端上传文件到应用服务器,然后应用服务器再把文件上传到OSS,如下图所示:
特点:上传慢。先上传到应用服务器,再上传到OSS
2.服务端签名后直传
采用javaScript端向服务端发起签名请求,获取参数后直传OSS
特点:客户端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。
3.服务端签名后直传并设置上传回调
客户端向服务端发起签名请求后,直传OSS然后OSS会回调对应的服务端回调接口,OSS回调完成后,应用服务器再返回结果给客户端。
特点:用户上传一个文件到OSS后,OSS会将上传结果返回给应用服务器,然后应用服务器会给OSS响应,然后OSS会将相关响应通知给客户端的用户。
综上:为阿里云的OSS官方给出的解决方案,简单分析了这几种方案。
经理所说的上传大文件失败的几率很大,并研究了代码后发现是用的第一种的上传方案,虽然使用了大文件的分块上传,但是并不建议使用,,
【正传】
这里是将后两种方案进行了拼合,具体写法见下文代码
因为博主为java开发文员,,所以就只放java代码吧,,至于前端代码,用上传控件将参数传对都可以,主要是服务端代码。
- 导入阿里云的jar
<dependency><groupId>com.aliyun.oss</groupId><artifactId>aliyun-sdk-oss</artifactId><version>2.8.3</version></dependency>
在编写代码的时候注意导入的jar是否为此jar,本文引入的为此jar,因为阿里云的jar更新迭代的几个版本有依赖关系。
因为我用的是SpringBoot项目,,所以就先建了个参数的bean实体,后续参数的获取请以项目的实际获取方式进行获取。
/*** Author: hzl* Date: 2018/11/27 15:40* Description: oss参数配置配置类*/ package com.ejsino.chat.config.aliyunoss;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component;/*** @author hzl* @create 2018/11/27* @Description: oss参数配置配置类* @since 1.0.0*/ @Component @ConfigurationProperties(prefix = "oss") public class OssConfigBean {private String bucketName;private String accessKeyId;private String accessKeySecret;private String endPoint;private String domainAddress;private String productClauseFilePath;private String tempFilePath;/*** oss参数获取打印* @return*/@Overridepublic String toString() {return "OssConfigBean{" +"bucketName='" + bucketName + '\'' +", accessKeyId='" + accessKeyId + '\'' +", accessKeySecret='" + accessKeySecret + '\'' +", endPoint='" + endPoint + '\'' +", domainAddress='" + domainAddress + '\'' +", productClauseFilePath='" + productClauseFilePath + '\'' +", tempFilePath='" + tempFilePath + '\'' +'}';}public String getBucketName() {return bucketName;}public void setBucketName(String bucketName) {this.bucketName = bucketName;}public String getAccessKeyId() {return accessKeyId;}public void setAccessKeyId(String accessKeyId) {this.accessKeyId = accessKeyId;}public String getAccessKeySecret() {return accessKeySecret;}public void setAccessKeySecret(String accessKeySecret) {this.accessKeySecret = accessKeySecret;}public String getEndPoint() {return endPoint;}public void setEndPoint(String endPoint) {this.endPoint = endPoint;}public String getDomainAddress() {return domainAddress;}public void setDomainAddress(String domainAddress) {this.domainAddress = domainAddress;}public String getProductClauseFilePath() {return productClauseFilePath;}public void setProductClauseFilePath(String productClauseFilePath) {this.productClauseFilePath = productClauseFilePath;}public String getTempFilePath() {return tempFilePath;}public void setTempFilePath(String tempFilePath) {this.tempFilePath = tempFilePath;} }
- OSS上传参数获取方式
在这里给那些初次看文档进行开发的码农说一下,,阿里给出的demo上边是将多组参数封装到了一个请求里边,,我这里是将他们分开来进行获取了,因为这些参数分为两种,一个是OSS初始化上传空间需要的参数,,一部分是灵活变动的签名参数,,还有,,因为签名具有实效性,所以每次在进行上传前都需要获取一下参数,,这里要特别注意一下,因为OSS存储文件的形式是重名文件默认覆盖原则,,所以在进行文件上传的时候一定要注意文件名的命名,,这里的写法是将文件名在服务端生成。首先,我们先获取初始化需要的参数
/*** @Description OSS上传需要的OSS服务参数获取* @Author hzl* @Date 2018/11/27* @Param []* @Return java.util.HashMap<java.lang.Stringjava.lang.Object>*/public HashMap<String, Object> generateOssParam() throws Exception {try {String domain = ossConfigBean.getDomainAddress();String dir = ossConfigBean.getProductClauseFilePath();String bucketName = ossConfigBean.getBucketName();String endPoint = ossConfigBean.getEndPoint();String accessKeyId = ossConfigBean.getAccessKeyId();String host = "http://" + bucketName + "." + endPoint.replace("http://", "");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 生成签名* @Author hzl* @Date 2018/11/27* @Param [callBack :要进行回调的参数,传入为空即默认为不进行回调]* @Return java.util.HashMap<java.lang.String java.lang.Object>*/public HashMap<String, Object> generateSign(String callBack) throws Exception {//获取上传oss需要的基本参数String endPoint = ossConfigBean.getEndPoint();String accessKeyId = ossConfigBean.getAccessKeyId();String accessKeySecret = ossConfigBean.getAccessKeySecret();String dir = ossConfigBean.getProductClauseFilePath();OSSClient client = null;try {//开启OSS客户端client = new OSSClient(endPoint, accessKeyId, accessKeySecret);long expireTime = 30;long expireEndTime = System.currentTimeMillis() + expireTime * 1000;//生成的到期时间转换位s,并转换为StringString 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);//根据到期时间生成policyDate expiration = new Date(expireEndTime);String postPolicy = client.generatePostPolicy(expiration, policyConditions);//生成signatureString postSignature = client.calculatePostSignature(postPolicy);//对policy进行UTF-8编码后转base64byte[] binaryData = postPolicy.getBytes("utf-8");String encodedPolicy = BinaryUtil.toBase64String(binaryData);//生成上传文件的文件名String fileName = MyConst.WEB_OSS_FILE + 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回调系统服务参数生成* @Author hzl* @Date 2018/11/27* @Param [callBackHost:要OSS进行回调的服务域名(不带http),* callBackInterFace:要进行回调的接口(oss上传结束进行请求的接口),* callBackBody:进行回调时携带的参数(以:key=value 的形式进行携带)]* @Return java.lang.String*/public String generateCallBack(String callBackHost, String callBackInterFace, String callBackBody) 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("callbackBodyType", "application/x-www-form-urlencoded");byte[] callBack = JSONObject.fromObject(callbackMap).toString().getBytes("utf-8");String callBackString = BinaryUtil.toBase64String(callBack);return callBackString;}
设置了回调请求的上传那么还需要一个OSS的回调接口
/*** @Description OSS上传回调* @Author hzl* @Date 2018/11/28* @Param [ossCallbackBody, authorization, publicKeyUrlBase64, request, response]* @Return com.ejsino.chat.config.domain.JSONResult*/@RequestMapping("/ossCallBack")public JSONResult callBack(@RequestBody String ossCallbackBody, @RequestHeader("Authorization") String authorization,@RequestHeader("x-oss-pub-key-url") String publicKeyUrlBase64, HttpServletRequest request,HttpServletResponse response) {boolean isCallBack = ossConfig.verifyOSSCallbackRequest(authorization, publicKeyUrlBase64, ossCallbackBody, request.getQueryString(), request.getRequestURI());if (isCallBack) {response.setStatus(HttpServletResponse.SC_OK);return JSONResult.success("success");} else {response.setStatus(HttpServletResponse.SC_BAD_REQUEST);return JSONResult.error("回调验证失败!");}}
/*** @Description OSS回调请求验证* @Author hzl* @Date 2018/11/27* @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 获取请求中的参数* @Author hzl* @Date 2018/11/27* @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 对请求参数进行规则校验* @Author hzl* @Date 2018/11/27* @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;}
至此,OSS上传就已经全部结束了。
OSS的文件直传因为要设置安全域名,,所以一定要先看官方文档,然后再看我的demo,在测试的时候也要特别的注意,因为有回调设置,所以需要公网或者内网穿透。
总结:这次写OSS文件上传代码中间也遇到了很多的问题,因为之前没有透彻理解官方源码,,也咨询过阿里的客服,,但是都没有彻底解决,在百度谷歌等搜索出来的也都是千篇一律,最终在跟一前辈的闲聊中得到了启发,有仔细琢磨了一下,终于唔出了其中的道理。愿,这边文章能够帮助更多像我这样码农,有不清楚的可以私信我。
OSS文件上传(页面直传)相关推荐
- SpringBoot整合阿里云OSS文件上传、下载、查看、删除
SpringBoot整合阿里云OSS文件上传.下载.查看.删除 该项目源码地址:https://github.com/ggb2312/springboot-integration-examples ( ...
- Springboot 搭建oss文件上传服务器
1.登录阿里云官网 https://www.aliyun.com/?spm=5176.8466032.top-nav.dlogo.724e1450B4jI0X 2.选择控制台 3. 找到对象存储oss ...
- 基于阿里云的OSS文件上传和下载
OSS概述 OSS是基于阿里云的一个云平台文件保存的系统,我们可以将服务器的文件上传至云端从而减轻服务器的压力. 初体验 首先创建一个bucket (给你的云储存器配置名字等基本信息) 生成Asses ...
- aliyun oss 文件上传 java.net.SocketTimeoutException Read timed out 问题分析及解决
aliyun oss 文件上传 java.net.SocketTimeoutException Read timed out 问题分析及解决 参考文章: (1)aliyun oss 文件上传 java ...
- js 导出pdf上传至oss_js实现oss文件上传及一些问题
关于兼容性问题,ie8以下的可以使用4.x的版本 一.引入sdk和jq 二.基本配置 var client =newOSS.Wrapper({ region:'', accessKeyId:'', a ...
- html页面选择附件实现,实现单文件上传,页面局部刷新_html/css_WEB-ITnose
实现单文件上传,页面不刷新 1.html fjFileInsert.action执行文件上传,然后返回数据到upLoad.html页面,upLoad.html页面在iframe中隐藏起来 当执行完毕, ...
- 轻松搞定阿里OSS文件上传和图片下载
轻松搞定阿里OSS文件上传和图片下载 目录 1. 阿里云oss账号准备 1.1 注册阿里云账号,并开通OSS存储服务 1.2 创建RAM子用户并使用子账户的AccessKeyId和Access ...
- 解决阿里云oss文件上传部分MP4格式视频文件上传导致上传崩溃问题
解决阿里云oss文件上传部分MP4格式视频文件上传导致上传崩溃问题 问题描述 java程序,使用阿里云oss文件上传服务,在测试时偶然发现,我用苹果手机开启高清进行摄像,将原图通过qq传到电脑上,在电 ...
- 阿里云oss文件上传工具类
阿里云oss文件上传工具类 阿里云oss 阿里云oss 导入文件阿里云oss的maven依赖 <!-- 阿里云oss依赖 --><dependency><groupId& ...
最新文章
- C# 创建、部署、调用WebService
- Hi3516A开发--apt-get更新
- 剑指 offer 编程题 C++ 版总结(下)
- 锁存器的工作原理_数字电路学习笔记(十):更多锁存器和触发器
- asm 5 java,java – 使用ASM(5.x)在字节代码中检测运行时的递归方法调用:howto?
- python库之numpy
- 最新百度翻译接口JS逆向教程
- matlab虚拟现实之使用V-Realm Builder2建模
- Idea搭建SpringCloud(四)------利用Feign实现负载均衡
- 音乐推荐系统(协同过滤和SVD)
- Struts2到底为我们做了什么
- IndentationError: expected an indented block缩进没问题但是出错
- C++第14周项目1 - 动物怎么叫
- php聚合话费充值怎么接,求充值话费接口
- R语言绘制Cleverland点图
- Delphi 2007 体验
- QQ空间十亿级视频播放技术优化揭密
- 2.支付10s倒计时
- linux中的网络命名空间的使用
- 关于计算机行业未来的发展