uniapp阿里云STS上传文件
需求
使用uniapp开发的时候就会遇到有上传图片视频之类的需求,但是我们实际的服务器带宽是有限的,而且如果是先上传到服务器在从服务器上传到OSS也是没有太大意义,所以我们就可以直接从APP里面将文件上传到OSS服务器,然后保存上传后的文件地址即可。
1
| 用户上传文件 ---> OSS服务器 ---> [得到文件的URL] ---> 后台服务器记录文件信息
|
问题所在
1. 我们使用uniapp作为APP开发工具,阿里云OSS提供的一些SDK不兼
一开始直接用官方提供node.js的SDK (ali-oss)
处理文件的时候是没有任何问题的,可以直接上传文件,且配置比较简单,只需要后台返回一些配置信息即可,但是后面我打包APP的时候发现出现错误,页面打不开;看日志也只能看到一些粗略的东西,大概意思就是里面使用了dom对象所以没办法初始化。如果是不需要打包app的话是可以直接使用的,可以参考这个文档 上传本地文件
2. 后端需要单独开发一个给前端调用OSS接口获取token的接口
意思就是我们不能直接把我们的accessKeyId 和 accessKeySecret
返回给前台调用,这样不安全。为此OSS也提供了临时访问凭证,配置起来比较麻烦,大家可以参考这个文档 使用STS临时访问凭证访问OSS;跟着文档一步一步来还是可以搞定的。
可能遇到的问题
1. OSS老是提示跨域
这里就正常参照文档提示的跨域配置即可,
参考官方提供的文档 设置跨域规则后调用OSS时仍然报“No ‘Access-Control-Allow-Origin’”的错误 ,
重点来了一定一定使用的上传的地址是你的Bucket域名
而非 Endpoint
这里我是被坑的最惨的时候了,老是提示跨域一直搞一直错。
2. uniapp的代码问题
就是导入库存在的各种问题,这里目前我遇到的就是使用js-base64
这个库的时候一直都是不行的,我也一直都没有搞定,找了别人的一段代码搞定的,不用 js-base64
代码部分
这里就只提供uniapp相关的前端代码,后端代码其实比较简答,主要就是各种阿里云上的配置比较麻烦,有需要可以照着文档一步一步来。
1. 需要引用的依赖
npm install js-base64
具体使用参考 https://www.npmjs.com/package/js-base64
npm install crypto-js
这两个都是用来做加密的,js-base64可以用或者不用
2. 前端代码
上传文件入口程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <template> <view> <button type="default" @click="selectImages">这是一个大大的按钮</button> </view> </template> <script>
import UploadOss from 'utils/UploadOss' export default { methods: {
selectImages() { uni.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['album'], success: function (res) { console.log(res); UploadOss.uploadFile(res).then(res => { console.log('上传文件的结果为:', res) }) } }); } } } </script>
|
base64.js
这里主要是因为我使用 js-base64
库会出问题, 如果你的不会则不需要此段代码,可以直接使用js-base64
的库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| const _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
const encode = function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; }
const decode = (input) => { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; if (input == undefined || input == null) {
} else { input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; }
}
const _utf8_encode = (string) => { string = string.replace(/\r\n/g, "\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if ((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; }
const _utf8_decode = (utftext) => { var string = ""; var i = 0; var c = c1 = c2 = 0; var c1 = 0; var c2 = 0; var c3 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; }
module.exports = { decode: decode, encode: encode };
|
上传文件JS文件
具体业务逻辑JS中有描述,这里不做赘述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
| const crypto = require('crypto-js')
const base64 = require('./base64s')
const tokenurl = 'https://baicu.com/gettoken';
const cacheKey = "xxxxtoken";
const validtimes = 1000 * 100;
export function uploadFile(fileobj) { const filename = fileobj.tempFiles[0].name; const filepath = fileobj.tempFilePaths[0]; return new Promise((resolve, reject) => { getFormData(filename).then(data => { uni.uploadFile({ url: data.host, filePath: filepath, fileType: "image", name: 'file', formData: data.formdata, success: res => { uni.hideLoading(); uni.showToast({title: '上传成功', icon: 'success', duration: 2000}); console.log(data.host + data.formdata.key); resolve(data.host + data.formdata.key); }, fail: err => { reject(err) uni.hideLoading(); uni.showModal({ content: err.errMsg, showCancel: false }); } }); }) }) }
const getFormData = (filename) => { return new Promise((resolve => { getToken().then(res => { console.log('获取到的token为', res) const ossAccessKeyId = res.accessKeyId; const securityToken = res.stsToken; const aliyunServerURL = 'https://' + res.bucket + '.' + res.endpoint; const policy = getPolicyBase64(5, 150) const signature = computeSignature(res.accessKeySecret, policy); const aliyunFileKey = getOssPath(filename)
const formdata = { key: aliyunFileKey, policy: policy, OSSAccessKeyId: ossAccessKeyId, 'x-oss-security-token': securityToken, success_action_status: '200', signature: signature }; let data = { host: aliyunServerURL, formdata: formdata } resolve(data); }) })) }
const getToken = () => { return new Promise((resolve, reject) => { const tempData = uni.getStorageSync(cacheKey); let isvalid = false; if (tempData && tempData !== '') { const tokenObj = JSON.parse(tempData); var diff = new Date().getTime() - Number.parseFloat(tokenObj.createtime); isvalid = diff < validtimes; }
if (isvalid) { resolve(JSON.parse(tempData)); } else { getToken4Api(cacheKey).then(res => { resolve(res); }) } }) }
const getToken4Api = (cacheKey) => { return new Promise((resolve, reject) => { uni.request({ url: tokenurl, success: (res) => { if (res.data.status === 200) { uni.setStorageSync(cacheKey, JSON.stringify(res.data.data)) resolve(res.data.data) } }, fail: (err => { reject(err); }) }) }) }
const getOssPath = (filename) => { const current_timestamp = new Date().getTime() const upload_name = crypto.MD5(current_timestamp + Math.random() * 10000) + filename; const current_day = dateformat('YYYYmmdd', new Date()) return 'files/' + current_day + '/' + upload_name }
const dateformat = (fmt, date) => { let ret; const opt = { "Y+": date.getFullYear().toString(), "m+": (date.getMonth() + 1).toString(), "d+": date.getDate().toString(), "H+": date.getHours().toString(), "M+": date.getMinutes().toString(), "S+": date.getSeconds().toString() }; for (let k in opt) { ret = new RegExp("(" + k + ")").exec(fmt); if (ret) { fmt = fmt.replace(ret[1], (ret[1].length === 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) } } return fmt; }
const getPolicyBase64 = function (timeout, maxsize) { const date = new Date() const timeOut = timeout || 1 const maxSize = maxsize || 10 date.setHours(date.getHours() + timeOut) const policyText = { expiration: date.toISOString(), conditions: [ ['content-length-range', 0, maxSize * 1024 * 1024] ] } return base64.encode(JSON.stringify(policyText)) }
const computeSignature = function (accessKeySecret, canonicalString) { return crypto.enc.Base64.stringify( crypto.HmacSHA1(canonicalString, accessKeySecret) ) }
module.exports = { uploadFile: uploadFile };
|
尾巴
uniapp上传文件到oss真的是搞死我了,一开始使用ali-oss我感觉so easy,但是打包才发现特别坑,查阅了网上很多的教程,大家描述的都挺模糊或者和我搞得模式不太一样,所以特意记录一下这个问题,希望能帮助有着和我一样问题的人。
鸣谢
小程序文件OSS直传阿里云
阿里云对象上传服务器签名