您的位置:首页 > 其它

对OSS存储服务的初步认识(上传,下载)

2016-07-13 17:22 627 查看
OSS存储服务
详细介绍请查看官方sdk文档:点击打开链接

说明:
参考手机端:加入用户体验改进计划



实际就是抓取系统logcat[包括异常],通过该日志回传给开发者,参照用户使用日志对系统的作出有效的改进。

触发点:广播

该功能涉及到1.监听系统捕捉日志
2.把日志上传到服务器

图示:



具体做法:
【一】Oss的存储【IntentService】

     准备:OSSAndroid SDK
     提示:文中的ID指的是AccessKey
ID,KEY指的是AccessKey Secret

1.解压后在libs目录下得到jar包,目前包括aliyun-oss-sdk-android-2.2.0.jar、okhttp-    

3.2.0.jar、okio-1.6.0.jar

2.将以上3个jar包导入工程的libs目录

3.引入lib后在

   AndroidManifest.xml文件中配置这些权限

<!--广播请求的权限-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

4.初始化OSSClient【来自SDK】

在IntentService中的onCreate()

初始化主要完成Endpoint设置、鉴权方式设置、Client参数设置。其中,鉴权方式包含明文设置模式、自签名模式、STS鉴权模式。鉴权细节详见后面的`访问控制`章节。

String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 明文设置secret的方式建议只在测试时使用,更多鉴权模式请参考后面的`访问控制`章节
OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider("<accessKeyId>", "<accessKeySecret>");
OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider);

以及需要创建断点记录文件夹

//断点数据保存的位置
Environment.getExternalStorageDirectory().getAbsolutePath() + "/oss_record/";
File recordDir = new File(recordDirectory);
// 要保证目录存在,如果不存在则主动创建
if (!recordDir.exists()) {
recordDir.mkdirs();
}

5.初始化之后就可以通过OSS上传文件,这里上传采用的分片上传。也就是说在上传过程中如果遇到网络突然中断了,oss会作相对应的记录保存上传的进度。当下一次重新上传该文件的时候直接从上次保存的进度开始

在onHandleIntent(Intentintent)中除了接收intent传递过来的path外还需要判断断点记录,如果存在首先把上一次的上传完毕后再处理当前的



至于上传具体细节请看SDKorDEMO中

还需要在清单文件中:注册
<!--服务-->
<service android:name=".myService"></service>


说明: 为何采用IntentService【一般采用它来下载,上传也不例外啦】
    IntentService是Service类的子类,用来处理异步请求。客户端可以通过startService(Intent)方法传递请求给IntentService。IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象(通过startService的方式发送过来的)所对应的任务,这样以免事务处理阻塞主线程。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。
  IntentService在处理事务时,还是采用的Handler方式,创建一个名叫ServiceHandler的内部Handler,并把它直接绑定到HandlerThread所对应的子线程。 ServiceHandler把处理一个intent所对应的事务都封装到叫做onHandleIntent的虚函数;因此我们直接实现虚函数onHandleIntent,再在里面根据Intent的不同进行不同的事务处理就可以了。

6.扩展模块就是文件的下载:
  由于实际开发中暂时不考虑该问题,所以该模块暂未实现.
  如有需求参考上传功能 ossSDK中也提供了该接口 :
  //创建保存的文件路径

finalFile f1 = new File(filepath + name);

if(!f1.exists()) {

f1.delete();

}

。。。。。。。。.

//初始化oss

  。。。。。。。。

//构造下载文件请求

GetObjectRequestget = new GetObjectRequest("fly","yao<span style="display: none; width: 0px; height: 0px;" id="transmark"></span>/test.txt");

OSSAsyncTasktask = oss.asyncGetObject(get, new    OSSCompletedCallback<GetObjectRequest,GetObjectResult>() {

@Override

publicvoid onSuccess(GetObjectRequest request, GetObjectResult result) {

//请求成功

//Log.d("Content-Length", "" +getResult.getContentLength());

InputStreaminputStream = result.getObjectContent();

    //通过outputStream直接输出文件【保存到本地】

OutputStreamos =null;

   //byte[1024]这个长度主要看文件大小来定

byte[]buffer = new byte[1024];

intlen;

try{

os= new FileOutputStream(f1);

while((len = inputStream.read(buffer)) != -1) {

//处理下载的数据

os.write(buffer,0, len);

}

}catch (IOException e) {

e.printStackTrace();

}finally{

if(inputStream!=null) {

try{

inputStream.close();

}catch (IOException e) {

e.printStackTrace();

}

}

if(os!=null);

try{

os.close();

}catch (IOException e) {

e.printStackTrace();

}

}

}

@Override

publicvoid onFailure(GetObjectRequest request, ClientExceptionclientExcepion, ServiceException serviceException) {

//请求异常

if(clientExcepion != null) {

//本地异常如网络异常等

clientExcepion.printStackTrace();

}

if(serviceException != null) {

//服务异常

Log.e("ErrorCode",serviceException.getErrorCode());

Log.e("RequestId",serviceException.getRequestId());

Log.e("HostId",serviceException.getHostId());

Log.e("RawMessage",serviceException.getRawMessage());

}

}

});


::其他的一些逻辑问题暂时不作判断。

【二】广播接收者
1.主要是接收处理logcat广播,启动服务。
2.注册:<!--广播-->静态注册:需要实时等待接收广播
<receiver android:name=".myReceiver" android:exported="true">
<intent-filter>
<!--接收开机发送的广播-->
<action    android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
<intent-filter>
<action android:name="fly.upload.logcat"></action>
</intent-filter>
<intent-filter>
<action android:name="fly.upload.logcat.isok"></action>
</intent-filter>
</receiver>


图示:
  


2.扩展模块:网络的监听,文件的监听
       网络监听:当没有网络的时候不开启服务,直接保存记录,发现有有网络的时候自动启动服务
       文件监听:监听本地断点文件记录有没有发生变化
【三】UI
   声明:(这个不是必须的)
   正常情况下个人认为在车机设置里添加
   这一项会比较人性化。

   应为考虑到上传可能需要使用用户的流量,在用户不知情下有必要提醒用户
   可以设置该功能默认是启动的。
   也就是说默认情况下是在监听用户使用情况。
   
   如果用户不开启该功能则不在上传日志文件。



                  

【oss附加功能】

1.在oss初始化化的时候还可以:

设置网络参数

也可以在初始化的时候设置详细的ClientConfiguration:

String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";//
明文设置
secret
的方式建议只在测试时使用,更多鉴权模式请参考后面的访问控制章节
OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider("<accessKeyId>", "<accessKeySecret>");

ClientConfiguration conf = new ClientConfiguration();

conf.setConnectionTimeout(15 * 1000); //
连接超时,默认
15
conf.setSocketTimeout(15 * 1000); // socket
超时,默认
15
conf.setMaxConcurrentRequest(5); //
最大并发请求书,默认
5
conf.setMaxErrorRetry(2); //
失败后最大重试次数,默认
2
OSS oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider, conf);

STS
鉴权模式

介绍

OSS可以通过阿里云STS服务,临时进行授权访问。阿里云STS(Security
Token Service) 是为云计算用户提供临时访问令牌的Web服务。通过STS,您可以为第三方应用或联邦用户(用户身份由您自己管理)颁发一个自定义时效和权限的访问凭证,App端称为FederationToken。第三方应用或联邦用户可以使用该访问凭证直接调用阿里云产品API,或者使用阿里云产品提供的SDK来访问云产品API。

您不需要透露您的长期密钥(AccessKey)给第三方应用,只需要生成一个访问令牌并将令牌交给第三方应用即可。这个令牌的访问权限及有效期限都可以由您自定义。

您不需要关心权限撤销问题,访问令牌过期后就自动失效。

以APP应用为例,交互流程如下图:

方案的详细描述如下:

App用户登录。App用户身份是客户自己管理。客户可以自定义身份管理系统,也可以使用外部Web账号或OpenID。对于每个有效的App用户来说,AppServer可以确切地定义出每个App用户的最小访问权限。

AppServer请求STS服务获取一个安全令牌(SecurityToken)。在调用STS之前,AppServer需要确定App用户的最小访问权限(用Policy语法描述)以及授权的过期时间。然后通过调用STS的AssumeRole(扮演角色)接口来获取安全令牌。角色管理与使用相关内容请参考《RAM使用指南》中的角色管理

STS返回给AppServer一个有效的访问凭证,App端称为FederationToken,包括一个安全令牌(SecurityToken)、临时访问密钥(AccessKeyId,AccessKeySecret)以及过期时间。

AppServer将FederationToken返回给ClientApp。ClientApp可以缓存这个凭证。当凭证失效时,ClientApp需要向AppServer申请新的有效访问凭证。比如,访问凭证有效期为1小时,那么ClientApp可以每30分钟向AppServer请求更新访问凭证。

ClientApp使用本地缓存的FederationToken去请求AliyunService
API。云服务会感知STS访问凭证,并会依赖STS服务来验证访问凭证,并正确响应用户请求。

【工作总结】

   从服务器上传下载文件这类型的开发,分2类把。

   第一类是第三方服务器【比如:阿里云,
mbon
等等】这些都有相对应的SDK 所有使用起来也挺方便 都为开发者提供了相对完整的框架 接口。开发者只需要申请对应的账号,获得相对应的key秘钥,把第三方的SDK引入到工程等,接着就是调用接口对数据库的增删改查了

   第二类就是自己搭建的本地服务器

代码:
MainActivity:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

switchListener();

}
/**
*
* 主要是对Switch控件的监听
* 事件的状态对应服务,广播的开启
* @author yao
* @time 16-7-12 下午5:34
*/
private void switchListener() {
Switch aSwitch = (Switch) findViewById(R.id.switch1);
aSwitch.setChecked(false);
if (myApplication.SWITCH_ON.equals(SPUtils.readData(myApplication.SPUTILS_NAME,myApplication.SPUTILS_KEY))) {
aSwitch.setChecked(true);
}
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if (b) {
Toast.makeText(MainActivity.this,myApplication.SWITCH_POINT, Toast.LENGTH_SHORT).show();
SPUtils.writeData(myApplication.SPUTILS_NAME,myApplication.SPUTILS_KEY, myApplication.SWITCH_ON);
initReceiver();
} else {
SPUtils.writeData(myApplication.SPUTILS_NAME,myApplication.SPUTILS_KEY, myApplication.SWITCH_OFF);
destoryReceiver();
}
}
});
}
/**
*停止服务
* @author yao
* @time 16-7-12 下午6:53
*/
private void destoryReceiver() {
Intent mintent = new Intent();
mintent.setAction("fly.upload.logcat.isok");
sendBroadcast(mintent);
}
/**
*启动广播【开机状态】【触发事件】
* @author yao
* @time 16-7-12 下午5:37
* 需要传递文件路径 才能上传服务器
*/
private void initReceiver() {
Log.d("123321", "initReceiver: 启动广播");
Intent intent = new Intent();
intent.putExtra("path", "null");
intent.setAction("fly.upload.logcat");
this.sendBroadcast(intent);
}[/code]
xml:

<android.support.v7.widget.CardView
android:elevation="10dp"
android:layout_margin="20dp"
android:layout_width="match_parent"
android:layout_height="56dp">
<Switch
android:layout_marginTop="10dp"
android:layout_marginLeft="20dp"
android:gravity="center|left"
android:layout_width="match_parent"
android:layout_height="38dp"
android:text="加入用户体验改进计划"
android:id="@+id/switch1"
android:layout_gravity="right" />
</android.support.v7.widget.CardView>
<TextView
android:gravity="center_horizontal"
android:textSize="16sp"
android:text="说明:这是一个 上传服务\n
通过广播和服务把文件提交到服务器中\n
广播地址:fly.upload.logcat\n
并且需要传文件路径\n
intent.setAction(‘fly.upload.logcat’);\n
intent.putExtra(key,value);\n
sendBroadcast(intent);\n"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

广播:
@Override
public void onReceive(Context context, Intent intent) {
Intent mintent = new Intent(context, myService.class);
if (("ON".equals(SPUtils.readData("CONFIG","LOGIN")))&&intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
//开机启动服务
mintent.putExtra("path", "");
context.startService(mintent);
}
if (("ON".equals(SPUtils.readData("CONFIG","LOGIN")))&&intent.getAction().equals("fly.upload.logcat")) {
String path = intent.getExtras().getString("path");
if (!path.equals("null")) {
mintent.putExtra("path", path);
context.startService(mintent);
}
}
}
服务:
public class myService extends IntentService{

private OSS oss;
private String recordDirectory;
private String action;

public myService() {super("myService");}
@Override
public IBinder onBind(Intent intent) {return super.onBind(intent);}
@Override
//初始化oss工作
public void onCreate() {init();super.onCreate();}
/*
* 注意点:
* 这里采用明文模式
* 实际中不可以采用
* */
private void init() {
String endpoint = "http://oss-cn-shanghai.aliyuncs.com";
// 明文设置AccessKeyId/AccessKeySecret的方式建议只在测试时使用
OSSCredentialProvider credentialProvider = new OSSPlainTextAKSKCredentialProvider("jSSSSS65SSSSS51", "SSSSDADASDSDSDSDASDDt");
oss = new OSSClient(getApplicationContext(), endpoint, credentialProvider);
//断点数据保存的位置
recordDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/oss_record/";
File recordDir = new File(recordDirectory);
// 要保证目录存在,如果不存在则主动创建
if (!recordDir.exists()) {
recordDir.mkdirs();
}
}
@Override
protected void onHandleIntent(Intent intent) {
//Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
action = intent.getExtras().getString("path");
String s = SPUtils.readData("PATH", "OLD");
//判断上一次是否存在没有上传完毕的数据,如果存在着首先上传,否则直接上传本次数据
if (!"".equals(s)) {
upLoad(s);
} else {
upLoad(action);
}
}
//上传服务
private void upLoad(final String path) {
// 创建断点上传请求,参数中给出断点记录文件的保存位置,需是一个文件夹的绝对路径 具体看sdk
ResumableUploadRequest request = new ResumableUploadRequest("fly", "fly/"+path, path, recordDirectory);
// 设置上传过程回调
request.setProgressCallback(new OSSProgressCallback<ResumableUploadRequest>() {
@Override
public void onProgress(ResumableUploadRequest request, long currentSize, long totalSize) {
//因为没有涉及到ui 这里不处理
Log.d("123321", "onProgress: "+currentSize);
}
});
OSSAsyncTask resumableTask = oss.asyncResumableUpload(request, new OSSCompletedCallback<ResumableUploadRequest, ResumableUploadResult>() {
@Override
public void onSuccess(ResumableUploadRequest request, ResumableUploadResult result) {
//成功后重置记录
if (path.equals(SPUtils.readData("PATH", "OLD"))) {
Intent mintent = new Intent();
mintent.setAction("fly.upload.logcat");
mintent.putExtra("path", action);
sendBroadcast(mintent);
}
SPUtils.writeData("PATH", "OLD", "");
}
@Override
public void onFailure(ResumableUploadRequest request, ClientException clientExcepion, ServiceException serviceException) {
// 请求异常
if (clientExcepion != null) {
// 本地异常如网络异常等
clientExcepion.printStackTrace();
}
if (serviceException != null) {
// 服务异常
Log.e("ErrorCode", serviceException.getErrorCode());
Log.e("RequestId", serviceException.getRequestId());
Log.e("HostId", serviceException.getHostId());
Log.e("RawMessage", serviceException.getRawMessage());
}
//失败后保存摘要,等待下次开机上传
SPUtils.writeData("PATH", "OLD", path);
}
});
}
@Override
public void onDestroy() {
oss = null;
recordDirectory = null;
super.onDestroy();
}

工具类:(需要的可以复制保存哦)
public class SPUtils {

/** 上下文 */
public static Context context;

public static void setContext(Context context) {
SPUtils.context = context;
}

/**
* 写入首选项文件(.xml)
* @param filename
* @param key
* @param value
*/
public static void writeData(String filename,String key,String value){
//实例化SharedPreferences对象,参数1是存储文件的名称,参数2是文件的打开方式,当文件不存在时,直接创建,如果存在,则直接使用
SharedPreferences mySharePreferences =
context.getSharedPreferences(filename, Context.MODE_PRIVATE);

//实例化SharedPreferences.Editor对象
SharedPreferences.Editor editor =mySharePreferences.edit();

//用putString的方法保存数据
editor.putString(key, value);

//提交数据
editor.commit();
}

/**
* 从首选项中读取值
* @param filename
* @param key
*/
public static String readData(String filename,String key){
//实例化SharedPreferences对象
SharedPreferences mySharePerferences =
context.getSharedPreferences(filename, Context.MODE_PRIVATE);

//用getString获取值
String name =mySharePerferences.getString(key, "");
return name;
}

/**
* 获取全部的键值对
* @param filename
* @return
*/
public static Map<String, ?> getAll(String filename)
{
SharedPreferences sp =
context.getSharedPreferences(filename,Context.MODE_PRIVATE);
return sp.getAll();
}

/**
* 查询某个key是否已经存在
* @param filename
* @param key
* @return
*/
public static boolean contains(String filename, String key)
{
SharedPreferences sp =
context.getSharedPreferences(filename,Context.MODE_PRIVATE);
return sp.contains(key);
}

/**
* 移除某个值
* @param filename
* @param key
*/
public static void remove(String filename, String key)
{
SharedPreferences sp = context.getSharedPreferences(filename,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove(key);
editor.commit();
}

}

APPLICATION:

public class myApplication extends Application {
public static final String SWITCH_ON = "ON";
public static final String SWITCH_OFF = "OFF";
public static final String SPUTILS_NAME = "CONFIG";
public static final String SPUTILS_KEY = "LOGIN";
public static final String SWITCH_POINT = "感谢";
/*   //文件路径名称
private final String LOGCAT_DIR = "logcat_flyaudio";
//内置路径
private final String PATH_HOME = "/storage/emulated/0";
//外置路径
private final String PATH_OTHER = "/storage/sdcard1";*/

//内置SDcard卡的路径
public static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator;

@Override
public void onCreate() {
super.onCreate();
SPUtils.setContext(this);
}
}

清单文件::::::::::::::::::::::::::::::::::::::::::::::::::::::
<!--广播请求的权限-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<application
android:name=".myApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--广播-->
<receiver android:name=".myReceiver" android:exported="true">
<intent-filter>
<!--接收开机发送的广播-->
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
<intent-filter>
<!--接收开机发送的广播-->
<action android:name="fly.upload.logcat"></action>
</intent-filter>
<intent-filter>
<!--接收开机发送的广播-->
<action android:name="fly.upload.logcat.isok"></action>
</intent-filter>
</receiver>
<!--服务-->
<service android:name=".myService"></service>
</application>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: