轻松搞定阿里OSS文件上传和图片下载 - Go语言中文社区

轻松搞定阿里OSS文件上传和图片下载


轻松搞定阿里OSS文件上传和图片下载





目录

1. 阿里云oss账号准备

  1.1 注册阿里云账号,并开通OSS存储服务

  1.2 创建RAM子用户并使用子账户的AccessKeyId和AccessKeySevcret进行业务操作,主账户存在安全问题

2. 项目中集成OSS依赖

3. 重写OSSFederationCredentialProvider从自家服务获取相关参数

4. OSSClient实例全局初始化

5. 封装和集成使用工具类,完成单文件上传,单文件断点续传、多文件上传等

6. OSSTokenServer 服务搭建(这个是模拟服务端产生token的,实际开发应该交给后台完成)

7. token获取支持HTTPS服务的优化和改进





1. 阿里云oss账号准备

1.1 注册阿里云账号,并开通OSS存储服务


1.2 创建RAM子用户并使用子账户的AccessKeyId和AccessKeySevcret进行业务操作,主账户存在安全问题

通过管理控制后台-访问控制 https://help.aliyun.com/product/28625.html

1.2.1 RAM控制台 https://ram.console.aliyun.com/

1.2.2 STS授权相关信息获取步骤:


2. 项目中集成OSS依赖

账号准备完成了,进入正题就是sdk的集成流程

module中添加依赖.配置完成后需同步一下

//阿里云OSS上传文件的SDK,上传内层依赖okhttp网络框架
compile 'com.aliyun.dpa:oss-android-sdk:+'
compile 'com.squareup.okhttp3:okhttp:3.11.0'
compile 'com.squareup.okio:okio:1.9.0'

proguard-rules.pro文件配置混线代码

#阿里云文件服务sdk
-keep class com.alibaba.sdk.android.oss.** { *; }
-dontwarn okio.**
-dontwarn org.apache.commons.codec.binary.**

3. 重写OSSFederationCredentialProvider从自家服务获取相关参数

由于移动端被认为是不可信的,因而重要参数是不可以存放在移动端的,这时候需要出发自己的服务器获取 AccessKeyId,AccessKeySecret,SecurityToken,Expiration 几个重要参数

触发请求自家服务器索要授权(这里我们的思路是放在OSSAuthCredentialsProvider中进行的)

  • 1, 发送自家网络请求,请求自家服务器索要授权
  • 2, 成功返回拿到需要AccessKeyId、AccessKeySecret、SecurityToken、Expiration
  • 3, 获取这些信息后,调用阿里SDK,构建OssClient上传即可

后端经返回如下代码

{
    "StatusCode": "200",
    "AccessKeyId": "STS.jp1jz2IHjp1jz2IH5Ef3jp1jz2IH5Ef3jp1jz2IH5Ef35Ef3",
    "AccessKeySecret": "BeoXtPg1lXbtPg1lXbttPg1lXbtIqqjp1j",
    "SecurityToken": "CAISkwJ1q6Ft5B22y2yyfSjIrok4uh5UUUQ7+EeP52yfSjIrokh5UytDWLIo/2yfSjIrok4uh5UdAIkq6NFHAm1D2yfSjIrok4uh5UK5yWzfg6wYvfw1NBcKq",
    "Expiration": "2019-02-16T04:16:01Z"
}

OSSAuthCredentialsProvider代码如下:

package com.ztsc.house.provider;

	import com.alibaba.sdk.android.oss.ClientException;
	import com.alibaba.sdk.android.oss.common.OSSConstants;
	import com.alibaba.sdk.android.oss.common.auth.OSSFederationCredentialProvider;
	import com.alibaba.sdk.android.oss.common.auth.OSSFederationToken;
	import com.alibaba.sdk.android.oss.common.utils.IOUtils;
	
	import org.json.JSONObject;
	
	import java.io.InputStream;
	import java.net.HttpURLConnection;
	import java.net.URL;
	
	/**
	 * Created by benchengzhou on 2019/2/15  16:25 .
	 * 作者邮箱: mappstore@163.com
	 * 功能描述: 阿里云服务上传获取上传参数表
	 * 类    名: OSSAuthCredentialsProvider
	 * 备    注:Authentication server issued under the agreement of the official website agreement, you can directly use the provider
	 */
	
	public class OSSAuthCredentialsProvider extends OSSFederationCredentialProvider {
	
	
	    private String mAuthServerUrl;
	    private com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder mDecoder;
	
	    public OSSAuthCredentialsProvider(String authServerUrl) {
	        this.mAuthServerUrl = authServerUrl;
	    }
	
	    /**
	     * set auth server url
	     *
	     * @param authServerUrl
	     */
	    public void setAuthServerUrl(String authServerUrl) {
	        this.mAuthServerUrl = authServerUrl;
	    }
	
	    /**
	     * set response data decoder
	     *
	     * @param decoder
	     */
	    public void setDecoder(com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder decoder) {
	        this.mDecoder = decoder;
	    }
	
	    @Override
	    public OSSFederationToken getFederationToken() throws ClientException {
	        OSSFederationToken authToken;
	        String authData;
	        try {
	            URL stsUrl = new URL(mAuthServerUrl);
	            HttpURLConnection conn = (HttpURLConnection) stsUrl.openConnection();
	            conn.setConnectTimeout(10000);
	            InputStream input = conn.getInputStream();
	            authData = IOUtils.readStreamAsString(input, OSSConstants.DEFAULT_CHARSET_NAME);
	            if (mDecoder != null) {
	                authData = mDecoder.decode(authData);
	            }
	            JSONObject jsonObj = new JSONObject(authData);
	            int statusCode = jsonObj.getInt("StatusCode");
	            if (statusCode == 200) {
	                String ak = jsonObj.getString("AccessKeyId");
	                String sk = jsonObj.getString("AccessKeySecret");
	                String token = jsonObj.getString("SecurityToken");
	                String expiration =/*"1000"*/ jsonObj.getString("Expiration");
	                authToken = new OSSFederationToken(ak, sk, token, expiration);
	            } else {
	                String errorCode = jsonObj.getString("ErrorCode");
	                String errorMessage = jsonObj.getString("ErrorMessage");
	                throw new ClientException("ErrorCode: " + errorCode + "| ErrorMessage: " + errorMessage);
	            }
	            return authToken;
	        } catch (Exception e) {
	            throw new ClientException(e);
	        }
	    }
	
	    public interface AuthDecoder {
	        String decode(String data);
	    }
	
	}


4. OSSClient实例全局初始化

使用之前进行全局初始化,一般选择将这段代码放在 Application 中进行

/**
 * 阿里对象存储服务初始化
 */
public void aliOSSInit() {

    
    String tokenHost = "http://192.168.1.120:7080/oss/getStsToken";

	 //官方文档建议,OSS的初始化应该放在子线程中进行。 原因大抵是每次token过期后Oss的SDK会默认通过 OSSAuthCredentialsProvider 从我们自己的后台服务器获取token,必然要进行网络请求,这样是一个耗时的操作需要放在子线程中进行
     new Thread(new Runnable() {
            @Override
            public void run() {
                //   demo 中使用这个     String endpoint = "http://img-cn-beijing.aliyuncs.com";
         
                //   String tokenHost = "http://192.168.1.120:7080";
				//特别提示这个是我们自定义的类,如果仅仅需要支持http可以使用OSS默认的OSSAuthCredentialsProvider,但是如果需要自定义token下载器或者token服务是HTTPS服务,请参考OSSAuthCredentialsProvider自定义,并导包为自定义类OSSAuthCredentialsProvider,切记
                OSSAuthCredentialsProvider credentialProvider = new OSSAuthCredentialsProvider(tokenHost);
                // OSSCredentialProvider credentialProvider = new OSSStsTokenCredentialProvider(ConstantValue.StsToken.AccessKeyId, ConstantValue.StsToken.SecretKeyId, ConstantValue.StsToken.SecurityToken);


                // 配置类如果不设置,会有默认配置。
                ClientConfiguration conf = new ClientConfiguration();
                conf.setConnectionTimeout(15 * 1000); // 连接超时,默认15秒。
                conf.setSocketTimeout(15 * 1000); // socket超时,默认15秒。
                conf.setMaxConcurrentRequest(5); // 最大并发请求数,默认5个。
                conf.setMaxErrorRetry(2); // 失败后最大重试次数,默认2次。
                oss = new OSSClient(sAppContext, ConstantValue.AliOSSconfig.endpoint, credentialProvider, conf);

                OSSLog.enableLog();  //调用此方法即可开启日志
            }
        }).run();
}


/**
 * 获取Oss
 *
 * @return
 */
public OSS getOss() {
    return oss;
}

其中 tokenHost 是后台服务提供给移动端获取 AccessKeyId,AccessKeySecret,SecurityToken,Expiration参数的服务地址,可以Android、Ios、pc通用一套

数据返回格式务必是严格的如下json形式

{
    "StatusCode": "200",
    "AccessKeyId": "STS.jp1jz2IHjp1jz2IH5Ef3jp1jz2IH5Ef3jp1jz2IH5Ef35Ef3",
    "AccessKeySecret": "BeoXtPg1lXbtPg1lXbttPg1lXbtIqqjp1j",
    "SecurityToken": "CAISkwJ1q6Ft5B22y2yyfSjIrok4uh5UUUQ7+EeP52yfSjIrokh5UytDWLIo/2yfSjIrok4uh5UdAIkq6NFHAm1D2yfSjIrok4uh5UK5yWzfg6wYvfw1NBcKq",
    "Expiration": "2019-02-16T04:16:01Z"
}



5. 封装和集成使用工具类,完成单文件上传,单文件断点续传、多文件上传等

android应用开发的最该境界是组件化开发,这样既能减少代码或模块间的耦合同时也不需要每个开发的成员都去熟悉第三方SDK的API和注意事项,极大的减少了开发成本

综合上面的原因,我对阿里的OSS进行了一次封装

主要完成以下功能**,喜欢的话点我下载完整API文档**

返回值类型 方法名称 方法描述
void upLoadSigleFile(java.lang.String filePath, OssService.ImageUpLoadCallback imageUpLoadCallback, boolean errRetry) 常规单个文件上传,请注意如果文件过大(相对网速考虑),请考虑使用
void resumableUploadBigFile(java.lang.String filePath, OssService.ImageUpLoadCallback imageUpLoadCallback, boolean errRetry) 单文件可断点续传操作,在网速不好或者文件比较大的情况下可以考虑使用
boolean isObjectExit(java.lang.String objectKey) 获取指定阿里oss的bucket中时候是否含有对应名称的文件对象
void deleteObject(java.lang.String objectKey, OssService.DeleteObjectCallback callback) 通过文件名删除指定地域对象存储服务器的对象,请注意不同地域相同文件名对象视为不同文件,无法同时删除
void copyObjectB2B(java.lang.String srcBucketName, java.lang.String srcObjectKey, java.lang.String destBucketName, java.lang.String destObjectKey, OssService.CopyB2BCallback bCallback) 将文件从一个存储空间(源存储空间)拷贝到同一地域的另一个存储空间(目标存储空间)中。
void asyncImagesUpLoad(java.util.ArrayList<java.lang.String> imgsList, boolean onErrorRetry, OssService.ImageUpLoadCallback imageUpLoadCallback) 多张图片一起上传服务器,并将上传后的图片路径按照对应的路径返回

同时也可以点击获取对应工具类代码

或获取完整文件


六、使用示例

/**
 * 上传图片
 */
public void pictureUpLoad() {
    ArrayList<String> imgs = new ArrayList<>();
    for (int j = 0; j < localMedias.size(); j++) {
        imgs.add(localMedias.get(j).getCompressPath());
    }

    new OssService(((MApplication) (MApplication.sAppContext)).getOss(), ConstantValue.AliOSSconfig.bucketName)
            .asyncImagesUpLoad(imgs, false, new OssService.ImageUpLoadCallback() {
                @Override
                public void onStart() {
			  		createLoadingDialog("正在上传");
                }

                @Override
                public void onFinish() {
					dissmissLoadingDialog();
                }

                @Override
                public void onSuccess(ArrayList<String> fileUrlList) {
					//调用在子线程UI刷新需要添加线程切换
                    LogUtil.e("文件上传完成");
                    StringBuffer buffer = new StringBuffer();
                    for (int i = 0; i < fileUrlList.size(); i++) {
                        buffer.append(fileUrlList.get(i)).append(",");
                    }
                    commitMessage(buffer.toString());  //信息上传
                }

                @Override
                public void onError(ClientException clientExcepion, ServiceException serviceException) {
					//调用在子线程UI刷新需要添加线程切换
                    ToastUtils.showToastShort("上传失败,请重试");
                }


                @Override
                public void onProgress(long currentSize, long totalSize) {
					//调用在子线程UI刷新需要添加线程切换
                    LogUtil.e("当前进度" + currentSize + "______" + totalSize);
                }
            });
}




6. OSSTokenServer 服务搭建(这个是模拟服务端产生token的,实际开发应该交给后台完成)

第三步中讲到,由于移动端被认为是不可信的,因而重要参数是不可以存放在移动端的,这时候需要出发自己的服务器获取 AccessKeyId,AccessKeySecret,SecurityToken,Expiration 几个重要参数,大师如果后台没有这个服务,那怎么办呢?这个呀我都给准备好了

按照下面的步骤轻松搭建 OSSTokenServer 服务

  • 1. 下载OSSTokenServer代码

  • 2.替换config.json中对应参数为自己家申请的参数

  • 3.使用IDEA运行代码

  • 4.设置对应端口

  • 5.点击运行

  • 6.网页访问测试,访问对应服务 比如http://127.0.0.1:7080,可以成功获取到结果

  • 7.测试通过后替换 Application 中的tokenHost,运行APP即完成



7. token获取支持HTTPS服务的优化和改进

上面的问题解决完成后我们发现已经完全服务我们日常的使用了,但是有时候公司获取OSS token的服务是HTTPS协议的,那这个时候就会出现问题,报错为支持的HTTPS,那这个问题曾么解决呢?

如果你知道了 OSSAuthCredentialsProvider对象其实就是OSS对Token过期下载一个管理的方式,他是当用户的token过期后,OSS主动调用 OSSAuthCredentialsProvidergetFederationToken 方法主动重新从我们的服务器获取TOKEN ,阿里的OSS SDK 默认为我们提供了一个 OSSAuthCredentialsProvider 类一般情况下我们直接使用这个就是可以的,没有必要自己单独去创建一个,但是特殊情况比如 希望使用自己的下载工具或者支持HTTPS获取Token等就需要使用到,自定义这个 OSSAuthCredentialsProvider 对象了。

明白了这些这个问题的解决其实很简单,我们只需要对 3 中的 OSSAuthCredentialsProvider 类文件中的代码做如下修改即可,需要强调的是 aliOSSInit 中 OSSAuthCredentialsProvider的导包一定是我们当前自定义的 OSSAuthCredentialsProvider

	/**
	 * Created by benchengzhou on 2019/2/15  16:25 .
	 * 作者邮箱: mappstore@163.com
	 * 功能描述: 阿里云服务上传获取上传参数表
	 * 类    名: OSSAuthCredentialsProvider
	 * 备    注:Authentication server issued under the agreement of the official website agreement, you can directly use the provider
	 */
	public class OSSAuthCredentialsProvider extends OSSFederationCredentialProvider {
	
	
	    private String mAuthServerUrl;
	    private com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder mDecoder;
	
	    public OSSAuthCredentialsProvider(String authServerUrl) {
	        this.mAuthServerUrl = authServerUrl;
	    }
	
	    /**
	     * set auth server url
	     *
	     * @param authServerUrl
	     */
	    public void setAuthServerUrl(String authServerUrl) {
	        this.mAuthServerUrl = authServerUrl;
	    }
	
	    /**
	     * set response data decoder
	     *
	     * @param decoder
	     */
	    public void setDecoder(com.alibaba.sdk.android.oss.common.auth.OSSAuthCredentialsProvider.AuthDecoder decoder) {
	        this.mDecoder = decoder;
	    }
	
	    @Override
	    public OSSFederationToken getFederationToken() throws ClientException {
	        OSSFederationToken authToken;
	        try {
				
				//这里可以替换自己的网络请求框架,但是注意一定要使用支持https的同步请求方法和配置
	            String url = BuildConfig.HOST_SERVICE_OSSTOKEN;
	            OkHttpClient okHttpClient = MApplicationInfo.getInstance().getSOkHttpClient();
	            final Request request = new Request.Builder()
	                    .url(url)
	                    .build();
	            final Call call = okHttpClient.newCall(request);
	            Response response = call.execute();
	            String data = response.body().string();
	            // LogUtil.e("---------------" + data);
	
	
	            JSONObject jsonObj = new JSONObject(data);
	            int statusCode = jsonObj.getInt("StatusCode");
	            if (statusCode == 200) {
	                String ak = jsonObj.getString("AccessKeyId");
	                String sk = jsonObj.getString("AccessKeySecret");
	                String token = jsonObj.getString("SecurityToken");
	 
	                String expiration =/*"1000"*/ jsonObj.getString("Expiration");
	                authToken = new OSSFederationToken(ak, sk, token, expiration);
	            } else {
	                String errorCode = jsonObj.getString("ErrorCode");
	                String errorMessage = jsonObj.getString("ErrorMessage");
	                throw new ClientException("ErrorCode: " + errorCode + "| ErrorMessage: " + errorMessage);
	            }
	            return authToken;
	        } catch (Exception e) {
	            e.printStackTrace();
	            throw new ClientException(e);
	        }
	    }
	
	    public interface AuthDecoder {
	        String decode(String data);
	    }
	}

注意事项

  • 如果我们按照上面的步骤操作了但是依旧报错,请从以下两个方面排查问题 OSSAuthCredentialsProvider对象

    • 1.OSS初始化时 OSSAuthCredentialsProvider 导包错误,应该导入自定义的

    • 2.在主线程中进行了网络请求(这里推荐OSS初始化在子线程中进行可避免此问题)


对于OkHttp3支持HTTPS详细过程请看这篇文章 Android-OKhttp解决https安全链接请求问题

这里仅简单叙述配置过程

1,新TrustAllcert类实现X509TrustManager接口:

public class TrustAllCerts implements X509TrustManager {  
    @Override    
    public void checkClientTrusted(X509Certificate[] chain, String authType) {}  

    @Override    
    public void checkServerTrusted(X509Certificate[] chain, String authType) {}  

    @Override    
    public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}    
}    
 
2,方法createSSLSocketFactory()调用类TrustAllcert,获取SSLSocketFactory:

private static SSLSocketFactory createSSLSocketFactory() {  
    SSLSocketFactory ssfFactory = null;  

    try {  
        SSLContext sc = SSLContext.getInstance("TLS");  
        sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());  

        ssfFactory = sc.getSocketFactory();  
    } catch (Exception e) {  
    }  

    return ssfFactory;  
}  

3,初始化OKHttpClient配置,注意请把这段代码放在application位置进行初始化:

OkHttpClient.Builder builder = new OkHttpClient.Builder();  
       builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);  
       builder.sslSocketFactory(createSSLSocketFactory());  
       builder.hostnameVerifier(new HostnameVerifier() {  
           @Override  
           public boolean verify(String hostname, SSLSession session) {  
               return true;  
           }  
       });  
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/bencheng06/article/details/88244171
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-28 03:52:51
  • 阅读 ( 1472 )
  • 分类:职场

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢