CodeSample小助手 2019-12-30
采用服务端签名后直传方案有个问题:大多数情况下,用户上传数据后,应用服务器需要知道用户上传了哪些文件以及文件名;如果上传了图片,还需要知道图片的大小等。为此OSS提供了上传回调方案。OSS回调完成后,应用服务器再将结果返回给客户端,以便服务端实时了解用户上传了什么文件。
当用户要上传一个文件到OSS,而且希望将上传的结果返回给应用服务器,这时就需要设置一个回调函数,将请求告知应用服务器。用户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户。
以下根据流程讲解核心代码和消息内容。
在客户端源码中的upload.js
文件中,如下代码片段的变量serverUrl
的值可以用来设置签名服务器的URL。设置好之后,客户端会向该serverUrl
发送GET请求来获取需要的信息。
// serverUrl是 用户获取 '签名和Policy' 等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
serverUrl = 'http://88.88.88.88:8888'
应用服务器侧的签名直传服务会处理客户端发过来的GET请求消息,您可以设置对应的代码让应用服务器能够给客户端返回正确的消息。各个语言版本的配置文档中都有明确的说明供您参考。
下面是签名直传服务返回给客户端消息body内容的示例,这个body的内容将作为客户端上传文件的重要参数。
{
"accessid":"6MKO******4AUk44",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDo1Mjoy******Jjdb25kaXRpb25zIjpbWyJjdb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
"signature":"VsxOcOudx******z93CLaXPz+4s=",
"expire":1446727949,
"callback":"eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9vc3MtZGVtby5hbGl5dW5jcy5jdb206MjM0NTAiLCJjYWxsYmFja0hvc3QiOiJvc3MtZGVtby5hbGl5dW5jcy5jdb20iLCJjYWxsYmFja0JvZHkiOiJmaWxlbmFtZT0ke29iamVjdH0mc2l6ZT0ke3NpemV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9JmhlaWdodD0ke2ltYWdlSW5mby5oZWlnaHR9JndpZHRoPSR7aW1hZ2VJdbmZvLndpZHRofSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==",
"dir":"user-dirs/"
}
上述示例的callback内容采用的是base64编码。经过base64解码后的内容如下:
{"callbackUrl":"http://oss-demo.aliyuncs.com:23450",
"callbackHost":"oss-demo.aliyuncs.com",
"callbackBody":"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
"callbackBodyType":"application/x-www-form-urlencoded"}
内容解析如下:
在客户端源码upload.js
文件中,callbackbody
的值是步骤2中应用服务器返回给客户端消息body中callback的内容。
new_multipart_params = {
'key' : key + '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status' : '200', //让服务端返回200,不设置则默认返回204
'callback': callbackbody,
'signature': signature,
};
客户端上传文件到OSS结束后,OSS解析客户端的上传回调设置,发送POST回调请求给应用服务器。消息内容大致如下:
Hypertext Transfer Protocol
POST / HTTP/1.1\r\n
Host: 47.97.168.53\r\n
Connection: close\r\n
Content-Length: 76\r\n
Authorization: fsNxFF0w******MNAoFb//a8x6v2lI1******h3nFUDALgku9bhC+cWQsnxuCo******tBUmnDI6k1PofggA4g==\r\n
Content-MD5: eiEMyp7lbL8KStPBzMdr9w==\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Date: Sat, 15 Sep 2018 10:24:12 GMT\r\n
User-Agent: aliyun-oss-callback\r\n
x-oss-additional-headers: \r\n
x-oss-bucket: signedcallback\r\n
x-oss-owner: 15447******20439\r\n
x-oss-pub-key-url: aHR0cHM6Ly9nb3NzcHVibGljLm******bi5jb20vY2FsbGJhY2tfcHViX2tleV92MS5wZW0=\r\n
x-oss-request-id: 5B9CDDCC9******88AE2BEA1\r\n
x-oss-requester: 15447******20439\r\n
x-oss-signature-version: 1.0\r\n
x-oss-tag: CALLBACK\r\n
eagleeye-rpcid: 0.1\r\n
\r\n
[Full request URI: http://47.xx.xx.53/]
[HTTP request 1/1]
[Response in frame: 39]
File Data: 76 bytes
HTML Form URL Encoded: application/x-www-form-urlencoded
Form item: "filename" = ".snappython.png"
Form item: "size" = "6014"
Form item: "mimeType" = "image/png"
Form item: "height" = "221"
应用服务器根据OSS发送消息中的authorization
来进行验证,如果验证通过,则向OSS返回如下json格式的成功消息。
String value: OK
Key: Status
下面举例介绍几个关键功能的代码实现:
若上传时采用固定格式的随机文件名,且后缀跟客户端文件名保持一致,可以将函数改为:
function check_object_radio() {
g_object_name_type = 'random_name';
}
如果想在上传时设置成用户的文件名,可以将函数改为:
function check_object_radio() {
g_object_name_type = 'local_name';
}
$dir ='abc/';
您可以利用Plupload的属性filters设置上传的过滤条件,如设置只能上传图片、上传文件的大小、不能有重复上传等。
var uploader = new plupload.Uploader({
……
filters: {
mime_types : [ //只允许上传图片和zip文件
{ title : "Image files", extensions : "jpg,gif,png,bmp" },
{ title : "Zip files", extensions : "zip" }
],
max_file_size : '400kb', //最大只能上传400KB的文件
prevent_duplicates : true //不允许选取重复文件
},
如果要知道文件上传成功后的文件名,可以用Plupload调用FileUploaded事件获取,如下所示:
FileUploaded: function(up, file, info) {
if (info.status == 200)
{
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = 'upload to oss success, object name:' + get_uploaded_object_name(file.name);
}
else
{
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response;
}
}
可以利用get_uploaded_object_name(file.name)
函数,得到上传到OSS的文件名,其中file.name记录了本地文件上传的名称。
JavaScript可以从服务端获取policyBase64、accessid、signature这三个变量,核心代码如下:
function get_signature()
{
// 判断expire的值是否超过了当前时间,如果超过了当前时间,就重新获取签名,缓冲时间为3秒。
now = timestamp = Date.parse(new Date()) / 1000;
if (expire < now + 3)
{
body = send_request()
var obj = eval ("(" + body + ")");
host = obj['host']
policyBase64 = obj['policy']
accessid = obj['accessid']
signature = obj['signature']
expire = parseInt(obj['expire'])
callbackbody = obj['callback']
key = obj['dir']
return true;
}
return false;
};
从服务端返回的消息解析如下:
{"accessid":"6MKO******4AUk44",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
"signature":"I2u57FWjTKqX/AE6doIdyff151E=",
"expire":1446726203,"dir":"user-dir/"}
解析Policy的内容如下:
{"expiration":"2015-11-05T20:23:23Z",
"conditions":[["content-length-range",0,1048576000],
["starts-with","$key","user-dir/"]]
上面Policy中增加了starts-with,用来指定此次上传的文件名必须以user-dir开头,用户也可自行指定。增加starts-with的原因是:在很多场景下,一个应用对应一个Bucket,为了防止数据覆盖,每个用户上传到OSS的文件都可以有特定的前缀。但这样存在一个问题,用户获取到这个Policy后,在失效期内都能修改上传前缀,从而上传到别人的目录下。解决方法为,在应用服务器端就指定用户上传文件的前缀。如果用户获取了Policy也没有办法上传到别人的目录,从而保证了数据的安全性。
在客户端源码upload.js
文件中,如下代码片段的变量serverUrl
的值可以用来设置签名服务器的URL,设置好之后,客户端会向该serverUrl
发送GET请求来获取需要的信息。
// serverUrl是 用户获取签名和Policy等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
serverUrl = 'http://88.88.88.88:8888'
前端如何实现批量上传文件?
OSS没有开放批量上传接口,如果需要批量上传,您可以使用一个循环去上传所有文件,示例与单个上传一致。