阿里云OSS图片上传实践
2018-06-09 本文已影响111人
十思叶
目前公司里的项目用到的文件管理服务都是阿里云OSS,下面介绍下Android端上传图片到阿里云OSS的具体实践。
阿里云OSS
1.阿里云OSS 移动应用方案请见官方文档基于OSS的移动开发
2.阿里云OSS Android SDK的介绍、安装、使用请见官方文档
上传图片流程
- 从APP服务端获取Token
- 通过Token上传图片到阿里云OSS
- 成功后通知APP服务端。

代码实现
上面流程中实现了三次网络通信的操作,在实践中,使用了RxJava的链式调用,简洁、明了。
APP服务端的接口
- 请求Token的接口
- 通知APP服务端上传成功的接口
interface AppServiceApi {
// ...
@GET("aliyun/sts")
fun getAliOSSSTS(@Header("X-Authorization") token: String,
@Query("right") right: String,
@Query("prod") prod: String
): Flowable<ApiResponse<AliOSSSTSModel>>
@PUT("users/v1/avatar")
fun notifyAvatarUpdate(@Header("X-Authorization") token: String
): Flowable<ApiResponse<Boolean?>>
// ...
}
object AppService {
private val API_BASE_URL = BuildConfig.SERVICE_PLATFORM
fun api(): AppServiceApi {
val retrofit = Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(API_BASE_URL)
.build()
return retrofit.create(AppServiceApi::class.java)
}
}
阿里云OSS的接口
上传图片的接口。其中将OSS SDK的方法改造成RxJava样式,便于链式调用。
object AliOSSService {
private val clientConfiguration = config()
private fun config(): ClientConfiguration {
val conf = ClientConfiguration()
conf.connectionTimeout = 15 * 1000 // 连接超时,默认15秒
conf.socketTimeout = 15 * 1000 // socket超时,默认15秒
conf.maxConcurrentRequest = 5 // 最大并发请求数,默认5个
conf.maxErrorRetry = 2 // 失败后最大重试次数,默认2次
return conf
}
fun uploadFile(context: Context, stsModel: AliOSSSTSModel, fileData: ByteArray): Flowable<ApiResponse<Boolean?>> {
val credentialProvider = OSSStsTokenCredentialProvider(
stsModel.credentials.accessKeyId,
stsModel.credentials.accessKeySecret,
stsModel.credentials.securityToken)
val oss = OSSClient(context, stsModel.endpoint, credentialProvider, clientConfiguration)
val put = PutObjectRequest(stsModel.bucket, stsModel.objectKey, fileData)
return Flowable.create({ subscriber ->
oss.asyncPutObject(put, object : OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
subscriber.onNext(ApiResponse(ApiCode.SUCCESS.code, true, "success"))
subscriber.onComplete()
}
override fun onFailure(request: PutObjectRequest?, clientException: ClientException?, serviceException: ServiceException?) {
subscriber.onError(ApiException(
code = if (serviceException == null) "10" else serviceException.errorCode,
message = serviceException?.rawMessage
))
}
})
}, BackpressureStrategy.LATEST)
}
}
将三个接口封装成一个,便于调用
object NetApi {
// avatar
fun uploadAvatar(context: Context, avatarData: ByteArray): Flowable<ApiResponse<Boolean?>> {
return AppService.api()
.getAliOSSSTS(token(),"WRITE", "OSS") // 获取Token
.flatMap { response ->
AliOSSService.uploadFile(context, response.data!!, avatarData)
} // 上传图片
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap {
AppService.api().notifyAvatarUpdate(token()) // 通知服务端
}
.compose(apply()) // 线程切换等后续处理
}
}
在上传图片的Activity中调用接口即可
private fun uploadAvatarWithToken(avatar: Drawable):Disposable {
val data = ImageUtils.drawable2Bytes(avatar, Bitmap.CompressFormat.PNG)
return NetApi.uploadAvatar(this, data)
.subscribe({
Util.toast().showShort(R.string.avatar_upload_success_prompt)
finish()
}, { t->
if (!NetErrorHelper.handleAuth(t, this)) {
Util.toast().showShort(R.string.avatar_upload_fail_prompt)
}
})
}