说明
因为IE低版本(比如IE8,IE9)对html5支持的不完善,为了在这些浏览器里面实现文件直传的功能,KS3提供PostObject接口,通过一个multipart/form-data 的格式,将文件上传到KS3服务器。采用Postobject方式提交数据,建议将生成签名的方法放在应用服务器上,前端传参获取签名。
本文重点介绍:Form表单上传
两种实现方式
方式一:后端计算签名(以nodejs为例)
1、后端服务代码
const ak = '***' // 替换成自己的ak
const sk = '***'// 替换成自己的sk
const endpoint = '***'
app.all('/post-policy', (req, res, next) => {
var query = req.query;
var now = Math.round(Date.now() / 1000);
var exp = now + 900;// 过期时间,可自行设置
var p = {
'expiration': new Date(exp * 1000).toISOString(),
// conditions配置,可根据需求更改,具体参考文档https://docs.ksyun.com/documents/948
'conditions': [
['eq', '$key', query.key],
["eq", "$bucket", query.bucketName]
]
}
// 上传成功后的回调地址
if(query.redirect) {
p.conditions.push(['eq', '$redirect', query.redirect])
}
var policy = JSON.stringify(p);
// 借助ks3 node sdk 生成signature
const signature = KS3.auth.getFormSignature(sk, policy)
res.json({
type: 'success',
result: {
url: `http://${query.bucketName}.${endpoint}`, // ks3服务地址
policyObj: JSON.parse(policy),// 为方便前端调试
policy: Buffer.from(policy).toString('base64'),
Ak: ak,
Signature: signature,
redirect: query.redirect
// securityToken: securityToken, // 如果使用临时密钥,则要返回
}
});
})
2、前端代码
```
<html>
<form id="form" target="submitTarget" action="" method="post" enctype="multipart/form-data" accept="*/*">
<input id="fileSelector" name="file" type="file">
<input id="submitBtn" type="button" value="提交">
</form>
<iframe id="submitTarget" name="submitTarget" style="display: none;" frameborder="0"></iframe>
<div id="msg"></div>
<script>
(function () {
var fileSelector = document.getElementById('fileSelector');
var form = document.getElementById('form');
var parseObj = function (obj) {
let str = Object.keys(obj).map(key => `${key}=${encodeURIComponent(obj[key])}`).join('&')
return str
}
// 获取签名
var getPostPolicyCredentials = function (opt, callback) {
// 替换成自己的服务地址
var url = 'http://127.0.0.1:3000/post-policy?' + parseObj(opt);
// fetch为自己对xmlhttpRequest的封装 可自行实现
fetch('get', url).then(data => {
callback('', data)
}).catch(err => {
callback('获取签名失败')
})
};
// 监听上传完成
var Key;
var submitTarget = document.getElementById('submitTarget');
var showMessage = function (err, data) {
console.log(err || data);
document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
};
submitTarget.onload = function () {
var search;
try {
if (submitTarget.contentWindow.location.href) {
showMessage('文件 ' + Key + ' 上传成功');
}
} catch (e) {
console.error('search: ', e)
showMessage('文件 ' + Key + ' 上传失败');
}
};
var setFormField = function (key, value) {
var el = document.getElementById(key);
if (!el) {
el = document.createElement('input');
el.hidden = true;
el.id = key;
el.name = key;
form.insertBefore(el, fileSelector);
}
el.setAttribute('value', value); // 需要保证 file 在表单最后
el.value = value;
};
// 发起上传
document.getElementById('submitBtn').onclick = function (e) {
var filePath = document.getElementById('fileSelector').value;
if (!filePath) {
document.getElementById('msg').innerText = '未选择上传文件';
return;
}
Key = 'post-' + filePath.match(/[\\\/]?([^\\\/]+)$/)[1]; // 这里指定上传目录和文件名
// 替换成自己的回调地址
var callbackUrl = location.origin + '/demo/empty.html'
// 获取签名保护字段
getPostPolicyCredentials({
key: Key,
redirect: callbackUrl
}, function (error, data) {
const res = JSON.parse(data)
const credentials = res.result
console.log('---> credentials: ',credentials)
// 添加form节点
setFormField('key', Key);
// 使用 policy 签名保护格式
credentials.securityToken && setFormField('X-Ksc-Security-Token', credentials.securityToken);
setFormField('KSSAccessKeyId', credentials.Ak);
setFormField('Signature', credentials.Signature);
setFormField('policy', credentials.policy);
setFormField('redirect', credentials.redirect);
form.action = credentials.url;
// 提交表单
form.submit();
});
};
})()
</script>
</html>
```
注:上述代码若想成功运行,自行实现getPostPolicyCredentials方法,替换ak、sk、endpoint、callbackUrl
3、运行结果
方式二:前端计算签名
依赖库:前端计算签名需要借助第三方库:
github.com/sytelus/Cry… 另外,还需base64将中文utf8编码具体的实现类,此处提供一份网上找到的源码仅供参考gitee.com/studyNeo/co…
示例代码:
<!doctype html>
<html lang="en">
<body>
<style>
#form {
display: inline-block;
margin-top: 10px;
border: 1px solid blue;
padding: 10px;
}
</style>
<form id="form" target="submitTarget" action="" method="post" enctype="multipart/form-data" accept="*/*">
<input id="fileSelector" name="file" type="file">
<input id="submitBtn" type="button" value="提交">
</form>
<iframe id="submitTarget" name="submitTarget" style="display: none;" frameborder="0"></iframe>
<div id="msg"></div>
<script type="text/javascript" src="../lib/crypto1/crypto/crypto.js"></script>
<script type="text/javascript" src="../lib/crypto1/hmac/hmac.js"></script>
<script type="text/javascript" src="../lib/crypto1/sha1/sha1.js"></script>
<script type="text/javascript" src="../lib/base64.js"></script>
<script>
(function () {
// 请求用到的参数
var ak = '' || '<your ak>'
var sk = '' || '<your sk>'
var bucketName = '<your bucket>'
var endpoint = '<your endpoint>' || 'ks3-cn-beijing.ksyuncs.com'
var Key = "post-403.png"; // 自行修改为上传的Key
// 回调地址 为了更新页面 需自行修改
var callbackUrl = location.origin + '/demo/empty.html'
var fileSelector = document.getElementById('fileSelector');
var form = document.getElementById('form');
var getSignatureParams = function () {
var now = Math.floor(Date.now() / 1000)
var exp = now + 900
console.log('now: ', now);
var policyText = {
"expiration": (new Date(exp * 1000)).toISOString(), //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
"conditions": [
["eq","$key",Key],
["eq", "$bucket", bucketName],
["eq", "$redirect", callbackUrl]
]
};
// 有关签名请详见文档https://docs.ksyun.com/documents/948
console.log('policyText: ', policyText);
var policyBase64 = Base64.encode(JSON.stringify(policyText))
console.log('policyBase64: ', policyBase64);
var bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, sk, { asBytes: true }) ;
var signature = Crypto.util.bytesToBase64(bytes);
console.log('signature: ', signature);
return {
policyBase64,
signature,
}
}
// 监听上传完成
var submitTarget = document.getElementById('submitTarget');
var showMessage = function (err, data) {
console.log(err || data);
document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
};
submitTarget.onload = function () {
var search;
try {
if (submitTarget.contentWindow.location.href) {
showMessage('文件 ' + Key + ' 上传成功');
}
} catch (e) {
console.error('search: ', e)
showMessage('文件 ' + Key + ' 上传失败');
}
};
var setFormField = function (key, value) {
var el = document.getElementById(key);
if (!el) {
el = document.createElement('input');
el.hidden = true;
el.id = key;
el.name = key;
form.insertBefore(el, fileSelector);
}
el.setAttribute('value', value); // 需要保证 file 在表单最后
el.value = value;
};
// 发起上传
document.getElementById('submitBtn').onclick = function (e) {
if (ak == '<your ak>' || sk == '<your sk>' || bucketName == '<your bucket>') {
alert('请修改配置参数')
return
}
var filePath = document.getElementById('fileSelector').value;
if (!filePath) {
document.getElementById('msg').innerText = '未选择上传文件';
return;
}
Key = 'post-' + filePath.match(/[\\\/]?([^\\\/]+)$/)[1]; // 这里指定上传目录和文件名
const param = getSignatureParams()
setFormField('key', Key);
setFormField('KSSAccessKeyId', ak);
setFormField('Signature', param.signature);
setFormField('policy', param.policyBase64);
setFormField('redirect', callbackUrl);
form.action = 'http://' + bucketName + '.' + endpoint;
// 提交表单
form.submit();
};
})();
</script>
</body>
</html>
运行结果:
注意事项
- 因方式二ak、sk会以明文的形式存在代码中,存在一定的安全隐患,推荐使用方式一。
- 获取signature时参与计算的policy字段与表单字段除指定外需要保持一致,不可遗漏,否则会报403,具体说明详见docs.ksyun.com/documents/9…