好处:
根据你们后端提供的get或者post API 请求获取policy直传的签名
接口一般定义为get请求 :/api/Upload/getSignedUrl
后端返回信息:
{
"accessid":"LTAI5tBDFVar1hoq****",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****",
"signature":"VsxOcOudx******z93CLaXPz+4s=",
"expire":1446727949,
"dir":"user-dirs/"
}
字段 | 描述 |
---|---|
accessid | 用户请求的AccessKey ID。 |
host | 用户发送上传请求的域名。 |
policy | 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。详情请参见Post Policy。 |
signature | 对Policy签名后的字符串。详情请参见Post Signature。 |
expire | 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)。 |
dir | 限制上传的文件前缀。 |
注意:dir是你要上传到oss bucket下的子目录名称,也就是你要上传的文件在哪个目录下,这个目录可以自己指定,如果这样,就可以客户端get请求的时候携带给后端,这时,API就可以按照个人需求来修改了
get请求可以在url上携带dir名称,也可以用post请求,body的形式传递json给后端,更容易参数扩展
dir目录很重要,在下面的第三点的post请求上传oss的时候,需要传递一个键值对:
“key” : value(dir+文件名)
用户使用Post方法向OSS发送文件上传请求
把上一步请求后端返回给你的那些参数,通过post请求构造body,上传至oss服务器
请求的地址就是服务端返回的host字段:http://post-test.oss-cn-hangzhou.aliyuncs.com
参数就是下面的伪代码:
new_multipart_params = {
// key表示上传到Bucket内的Object的完整路径,例如exampledir/exampleobject.txtObject,完整路径中不能包含Bucket名称。
// filename表示待上传的本地文件名称。
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
// 设置服务端返回状态码为200,不设置则默认返回状态码204。
'success_action_status' : '200',
'signature': signature,
};
注意:这里每一个参数都不能少,success_action_status不传也行,但是OSS默认会返回204状态码,指定200以后,会返回200
这里通过apiPost进行上传测试:
红框内是必传字段,我通过测试得出两个结论:
1.policy签名直传方式中,这个key一定要传递,指定好上面说的那个dir名称,否则会报错,类似policy策略失败问题
2.这种方式不会像我们正常post请求后端那样支持批量上传文件了,因为有key的限制
我尝试了多个file配置多个key,以失败告终
fun uploadOssFile(
context: Context,
signature:String,
endpoint: String,
bucketName: String,
objectName: String,
uploadFilePath: String
) {
credentialProvider = object : OSSCustomSignerCredentialProvider() {
override fun signContent(content: String): String {
// 按照OSS规定的签名算法加签字符串,并将得到的加签字符串拼接AccessKeyId后返回。
// 将加签的字符串传给您的服务器,然后返回签名。如果因某种原因加签失败,服务器描述错误信息后返回null。
Logger.d("signature=$signature") //这里直接用后端返回的签名
return signature
}
}
conf = ClientConfiguration()
conf!!.connectionTimeout = 15 * 1000 // 连接超时,默认15秒
conf!!.socketTimeout = 15 * 1000 // socket超时,默认15秒
conf!!.maxConcurrentRequest = 5 // 最大并发请求数,默认5个
conf!!.maxErrorRetry = 2 // 失败后最大重试次数,默认2次
oss = OSSClient(context, endpoint, credentialProvider, conf)
// 构造上传请求
val putObjectRequest = PutObjectRequest(bucketName, objectName, uploadFilePath)
//同步
putObjectSync(putObjectRequest, bucketName, objectName)
// 异步上传时可以设置进度回调
putObjectRequest.progressCallback = OSSProgressCallback { request, currentSize, totalSize ->
d(
"PutObject, currentSize: $currentSize totalSize: $totalSize"
)
}
val ossAsyncTask: OSSAsyncTask<*> = oss!!.asyncPutObject(putObjectRequest,
object : OSSCompletedCallback<PutObjectRequest, PutObjectResult> {
override fun onSuccess(request: PutObjectRequest?, result: PutObjectResult?) {
Log.e("url=: "+oss!!.presignPublicObjectURL(bucketName,objectName) )
Log.d("PutObject,UploadSuccess" + "")
Log.d("ETag:" + result?.eTag)
Log.d("RequestId:" + result?.requestId)
aliyunUploadCallBack.UploadSuccess(
oss!!.presignPublicObjectURL(
bucketName,
objectName
)
)
}
override fun onFailure(
request: PutObjectRequest?,
clientException: ClientException?,
serviceException: ServiceException?
) {
//注:ClientException指客户端尝试向OSS发送请求以及数据传输时遇到的异常。例如,当发送请求时网络连接不可用,则会抛出ClientException。当上传文件时发生IO异常,也会抛出ClientException。
// 请求异常。
Logger.d("onFailure----")
if (clientException != null) {
// 本地异常,如网络异常等。
clientException.printStackTrace()
e("网络异常,$clientException")
aliyunUploadCallBack.UploadFail("网络异常")
}
//注:ServiceException指服务器端错误,来源于对服务器端错误信息的解析。OSSException包含OSS返回的错误码和错误信息
if (serviceException != null) {
// 服务异常。
Log.e("服务异常,$serviceException") //OSS返回的错误码。
Log.e("ErrorCode:" + serviceException.errorCode)
Log.e("message:" + serviceException.message) //OSS返回的详细错误信息。
Log.e("RequestId:" + serviceException.requestId) //用于唯一标识该请求的UUID。您可以凭借此RequestId请求协助,排查并解决您遇到的问题。
Log.e("HostId:" + serviceException.hostId) //用于标识访问的OSS集群,与请求时使用的Host一致。'
Log.e("RawMessage:" + serviceException.rawMessage) //HTTP响应的原始Body文本
aliyunUploadCallBack.UploadFail("服务异常")
}
}
})
interface AliyunUploadCallBack {
fun UploadSuccess(url: String?)
fun UploadFail(error: String?)
}
post自行请求上传方式,这种方式没有通过阿里云API调用,所以要提前在阿里云管理平台bucket下面创建文件夹
我们当然希望后续根据不同的业务去创建不同的目录了,所以后来又采用OSS提供API方式,put Object到OSS,这种方式OSS会先判断有没有这个目录,有就不管了直接上传文件,没有就给我们创建一个指定的目录
我们上传到OSS的文件,没设置callback的话,后端并不知道,有两种方法可以同步后端;
方案一:在请求OSS的时候,设置callback,估计oss上传完成会通知后端?
方案二:也是我现在项目做的方式,我们拿到OSS返回200以后,跟后端重新定义接口同步文件信息到后端
报错信息 | 解决方案 |
---|---|
204 | 在formData: {}中添加 success_action_status:“200” |
400 | 添加上传的 Key 值,路径也要写正确,/images/test.png 为错误写法(最前面多了个斜杠),正确写法success images/test.png ;另外,阿里云OSS一次只允许上传一张照片。 |
403 | 检测 OSSAccessKeyId,policy,signature 是否填写正确; expiration已过期 |
405 | URL中添加服务器地址 http://post-test.oss-cn-hangzhou.aliyuncs.com 而不能写成 http://post-test.oss-cn-hangzhou.aliyuncs.com/images/test.png这种形式 |
1.om.alibaba.sdk.android.oss.ClientException: This task is cancelled!
问题解决:任务可能提前取消了,删除任务取消的代码即可解决
类似这种代码注释掉:
// ossAsyncTask.cancel(); // 可以取消任务
// ossAsyncTask.waitUntilFinished(); // 等待任务完成
2.oss安卓自签名上传文件 oss!!.asyncPutObject 无结果,不调用成功,也不调用失败回调?
问题解决:查看代码用的是不是kotlin,如果是,会有跟java 不匹配的问题,结果参数加上?来匹配java返回的空
3.PutObjectRequest上传文件遇到的错误,报错信息:
服务异常,[StatusCode]: 400, [Code]: InvalidBucketName, [Message]: The specified bucket is not valid., [Requestid]: 64B4AF843218A13939FAA184, [HostId]: xxx.xxx.oss-cn-xxx.aliyuncs.com, [RawMessage]: <?xml version="1.0" encoding="UTF-8"?>
<Error> <Code>InvalidBucketName</Code> <Message>The specified bucket is not valid.</Message> <RequestId>64B4AF843218A13939FAA184</RequestId> <HostId>xxx.xxx.oss-cn-xxx.aliyuncs.com</HostId> <BucketName>xxx.xxx</BucketName> <EC>0015-00000001</EC> </Error>
问题解决:
排查了一下自己的putObject需要传的参数bucketName没错啊,为什么报bucketName无效呢?
最终的答案是putObject的传入参数endpoint传错了,误认为后端给返回的host参数是endpoint,
要么在获取OSS配置接口让后端给你返回这个参数,要嚒,查看OSS平台配置看你的endpoint是什么,客户端写死就行
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, uploadFilePath);
阿里云服务端签名直传文档
https://help.aliyun.com/document_detail/31926.html?spm=a2c4g.31920.0.0