温馨提示:这篇文章已超过410天没有更新,请注意相关的内容是否还可用!
阿里云OSS提供小程序文件直传功能,支持微信小程序、H5及PC端web使用。该功能允许用户直接上传文件至阿里云OSS存储,简化了文件存储流程。这一服务适用于各种场景,如图片、视频等文件的存储和分享。它提供了便捷的文件管理,提高了文件传输效率和存储安全性。无论是小程序开发者还是普通用户,都能通过该功能轻松实现文件上传和存储。
支持微信小程序、H5、PC端web使用,整套文件进行封装统一使用
开发背景:做类似发布朋友圈的功能需求,由于后端对发布功能只提供一个接口,文字、状态、文件上传统一一个接口上传,且对文件上传方面做的接口存在诸多问题(人已经整麻了),包括各种数据结构的转换迎合后端,为节省时间和甩锅,被迫从客户端直传阿里云服务器,绕开服务端进行文件上传等操作,中间base64处理、加密策略,计算签名等处理都在前端完成。
优点:减少服务器压力
缺点:客户端目前不能直接预览文件,还需进一步在客户端处理(还在研究中)
具体做法见官方文档:如何在微信小程序环境下将文件上传到OSS_对象存储-阿里云帮助中心
一.文件解释
二.配置文件代码
config.js
let fileHost = "https://xxxxxx.oss-cn-beijing.aliyuncs.com"; //阿里云OSS的文件根域名 let config = { //aliyun OSS config uploadImageUrl: `${fileHost}`, //默认存在根目录,可根据需求改 AccessKeySecret: 'xxxxxxxx', // 这里的配置找运维或者阿里云OSS的管理者要对应的账号和密钥就可以了 OSSAccessKeyId: 'xxxxxxxx', // 这里的配置找运维或者阿里云OSS的管理者要对应的账号和密钥就可以了 timeout: 87600 //这个是上传文件时Policy的失效时间 }; module.exports = config
const Base64 = { // private property _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", // public method for encoding encode: function (input) { var output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0; input = Base64._utf8_encode(input); while (i > 2; enc2 = ((chr1 & 3) > 4); enc3 = ((chr2 & 15) > 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); } return output; }, // public method for decoding decode: function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i 4); chr2 = ((enc2 & 15) > 2); chr3 = ((enc3 & 3) 127) && (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; }, // private method for UTF-8 decoding _utf8_decode: function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i 191) && (c b); }, // Swap big-endian to little-endian and vice versa endian: function (n) { // If number given, swap endian if (n.constructor == Number) { return util.rotl(n, 8) & 0x00FF00FF | util.rotl(n, 24) & 0xFF00FF00; } // Else, assume array and swap all items for (var i = 0; i 0; n--) bytes.push(Math.floor(Math.random() * 256)); return bytes; }, // Convert a string to a byte array stringToBytes: function (str) { var bytes = []; for (var i = 0; i >> 5] |= str.charCodeAt(c) >> 5] |= bytes[i] >> 5] >>> (24 - b % 32)) & 0xFF); return bytes; }, // Convert a byte array to a hex string bytesToHex: function (bytes) { var hex = []; for (var i = 0; i >> 4).toString(16)); hex.push((bytes[i] & 0xF).toString(16)); } return hex.join(""); }, // Convert a hex string to a byte array hexToBytes: function (hex) { var bytes = []; for (var c = 0; c >> 2)); overflow = (bytes[i] & 0x3) >> 4))); overflow = (bytes[i] & 0xF) >> 6))); base64.push(base64map.charAt(bytes[i] & 0x3F)); overflow = -1; } } // Encode overflow bits, if there are any if (overflow != undefined && overflow != -1) base64.push(base64map.charAt(overflow)); // Add padding while (base64.length % 4 != 0) base64.push("="); return base64.join(""); }, // Convert a base-64 string to a byte array base64ToBytes: function (base64) { // Use browser-native function if it exists if (typeof atob == "function") return util.stringToBytes(atob(base64)); // Remove non-base-64 characters base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); var bytes = []; for (var i = 0; i > 4)); break; case 2: bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) >> 2)); break; case 3: bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) hasher._blocksize * 4 ? hasher(key, { asBytes: true }) : util.stringToBytes(key); // XOR keys with pad constants var okey = key, ikey = key.slice(0); for (var i = 0; isha1.js
const Crypto = require('./crypto.js'); (function(){ // Shortcut var util = Crypto.util; // Public API var SHA1 = Crypto.SHA1 = function (message, options) { var digestbytes = util.wordsToBytes(SHA1._sha1(message)); return options && options.asBytes ? digestbytes : options && options.asString ? util.bytesToString(digestbytes) : util.bytesToHex(digestbytes); }; // The core SHA1._sha1 = function (message) { var m = util.stringToWords(message), l = message.length * 8, w = [], H0 = 1732584193, H1 = -271733879, H2 = -1732584194, H3 = 271733878, H4 = -1009589776; // Padding m[l >> 5] |= 0x80 >> 9) > 31); } var t = ((H0 >> 27)) + H4 + (w[j] >>> 0) + ( j > 2); H1 = H0; H0 = t; } H0 += a; H1 += b; H2 += c; H3 += d; H4 += e; } return [H0, H1, H2, H3, H4]; }; // Package private blocksize SHA1._blocksize = 16; })(); module.exports = Crypto;readme.md
# 上传文件到阿里云 ## 说明 > 开发框架是uniapp,项目实战,打包到微信小程序及H5都是可以正常上传的。 ## 配置说明 > 只需要在config.js中,添加相关的配置就可以使用了。 ## 上传阿里云的文件目录地址 > 可按环境区分,也可以写死,根据自己项目需求配置。batchUploadAliyun.js中集成了单个文件上传及多个文件上传的方法封装。 ## 上传文件的入参示例 > {"type": 1, "tempFilePath": "xxx.png", "":""},type是根据自己业务定义的文件类型。如不需要可删除该参数; ## 出参示例 > 由于上传阿里云OSS只返回成功或者失败状态,所以代码中,在成功后,手动拼接了完整的url返回出来。{"type":1, "tempFilePath": "xxx.png", "url":"手动拼接的完整url"} ## 疑问解答 > 如有疑问或不理解的地方,可私信我,共同探讨。三.核心代码
1.h5、pc端代码
const uploadAliOss = require('./uploadAliyun.js'); let env = process.env.NODE_ENV; const dirObj = { 1: env + '/img/', 2: env + '/video/' }; // 批量上传文件到阿里云oss; export function batchUploadAliyun(arr){ return new Promise(async (resolve, reject) => { let result = []; for( let i=0; i { result.push(res); }); } resolve(result); }).catch(err => { reject(err) }); } // 上传单个文件到阿里云oss; export function uploadfileAliyun(fileInfo){ return new Promise((resolve, reject) => { const type = fileInfo['type']; let filePath = fileInfo['tempFilePath']; uploadAliOss({ filePath: filePath, dir: dirObj[type], success: function (res) { let result = {}; Object.keys(fileInfo).forEach(it => { if(it !== 'path' && it !== 'tempFilePath'){ result[it] = fileInfo[it]; } }); result['url'] = res; resolve(result); }, fail: function (err) { reject(err); } }) }).catch(err => { reject(err) }); }2.微信小程序
const env = require('./config.js'); const Base64 = require('./Base64.js'); require('./hmac.js'); require('./sha1.js'); const Crypto = require('./crypto.js'); const uploadFile = function (params) { if (!params.filePath) { uni.showModal({ title: '文件错误', content: '请重试', showCancel: false }); uni.hideLoading(); return false; } const aliyunFileKey = params.dir; const aliyunServerURL = env.uploadImageUrl; const accessid = env.OSSAccessKeyId; const policyBase64 = getPolicyBase64(); const signature = getSignature(policyBase64); uni.uploadFile({ url: aliyunServerURL, filePath: params.filePath, name: 'file', formData: { 'key': aliyunFileKey, 'policy': policyBase64, 'OSSAccessKeyId': accessid, 'signature': signature, 'success_action_status': '200' }, success: function (res) { console.log('成功回调',res); if (res.statusCode != 200) { if(params.fail){ params.fail(res) } return; } if(params.success){ let result = aliyunServerURL + '/' + aliyunFileKey; params.success(result); } }, fail: function (err) { err.wxaddinfo = aliyunServerURL; if (params.fail) { params.fail(err) } } }) } const getPolicyBase64 = function () { let date = new Date(); date.setHours(date.getHours() + env.timeout); let srcT = date.toISOString(); const policyText = { "expiration": srcT, //设置该Policy的失效时间 "conditions": [ ["content-length-range", 0, 30 * 1024 * 1024] // 设置上传文件的大小限制,30mb ] }; const policyBase64 = Base64.encode(JSON.stringify(policyText)); return policyBase64; } const getSignature = function (policyBase64) { const accesskey = env.AccessKeySecret; const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, { asBytes: true }); const signature = Crypto.util.bytesToBase64(bytes); return signature; } module.exports = uploadFile;四.小程序实战运用
仿上述小程序代码,根据实际要求封装
注意:微信小程序配置域名白名单,需要在微信开发者配置(官网),以实现微信小程序和OSS Bucket之间的正常通信。
1.小程序上传至阿里云服务器必传参数
参数 | 是否必选 | 说明 |
host | 是 | 填写Bucket的访问域名,例如https://examplebucket.oss-cn-zhangjiakou.aliyuncs.com。如果您的Bucket已绑定自定义域名,建议填写您的自定义域名。 |
signature | 是 | 填写步骤3 中获取到的signature信息。 |
ossAccessKeyId | 是 | 填写您的AccessKey ID。如果您是通过STS获取的临时用户,则填写临时用户的AccessKey ID。 |
policy | 是 | 填写步骤3 中获取到的policy信息。 |
key | 是 | 设置文件上传至OSS后的文件路径。例如,您需要将myphoto.jpg上传至test文件夹下,则填写test/myphoto.jpg。 |
securityToken | 是 | 如果使用STS认证,则填写步骤3 中使用客户端签名获取到的SecurityToken。 |
filePath | 是 | 填写待上传文件的本地完整路径,例如D:\example.txt。 |
2.效果图
2.代码块
+ import env from '../../utils/OssUpload/config.js' import Base64 from '../../utils/OssUpload/Base64.js' import Crypto from '../../utils/OssUpload/crypto.js' require('../../utils/OssUpload/hmac.js') require('../../utils/OssUpload/sha1.js') export default { data() { return { arrImg: [], imgUrl: '', imgUrlObj: [], } }, methods: { // About Change Avatar of users // 1.换头像的途径菜单 wx.showActionSheet,选择菜单 // 调用wx.showActionSheet,作选择菜单 changeAvatarSheet() { var that = this wx.showActionSheet({ itemList: ['从相册中选择', '拍照'], itemColor: '', success: function (res) { console.log('res', res) if (!res.cancel) { if (res.tapIndex == 0) { // 调用函数 that.chooseWxImageShop('album') } else if (res.tapIndex == 1) { //手机拍照 // console.log(res.tapIndex); // 调用函数 that.chooseWxImageShop('camera') } } }, }) }, // 2.wx.chooseImage // 3.上传头像到数据库 wx.uploadFile chooseWxImageShop: function (type) { // console.log('1111', uploadfileAliyun) wx.chooseMedia({ count: 4, sizeType: ['original', 'compressed'], sourceType: [type], success: (res) => { console.log('params', res) const params = res.tempFiles[0].tempFilePath if (!params) { uni.showModal({ title: '文件错误', content: '请重试', showCancel: false, }) uni.hideLoading() return false } //获取年月日 const date = new Date() const year = date.getFullYear() const month = date.getMonth() + 1 ', res) if (res.statusCode != 200) { if (params.fail) { params.fail(res) } return } wx.showToast({ title: '添加图片成功', }) // if (params.success) { // let result = aliyunServerURL + '/' + aliyunFileKey // params.success('成功回调-拼接路径', result) // } //文件名做个保存,后端需要根据这个文件名来获取图片 // console.log('sdkUrl',sdkUrl); // console.log('this.Url', this.Url) }, fail: function (err) { console.log('err-->', err) err.wxaddinfo = aliyunServerURL if (params.fail) { params.fail(err) } wx.showToast({ title: '添加图片失败', }) }, }) // const imgUrlObj= { // id: 0, // src: aliyunFileKey // } this.arrImg.push(aliyunFileKey) //用map创建一个对象,然后把对象循环遍历放入数组中,且有唯一的id this.imgUrlObj = this.arrImg.map((item, index) => { return { id: index, src: aliyunFileKey, } }) console.log('imgUrlObj', this.imgUrlObj); // this.arrImg.push(imgUrlObj) return this.$emit('changeUrl', this.imgUrlObj) }, }) }, //上传到阿里云 // 上传单个文件到阿里云oss; getPolicyBase64: function () { let date = new Date() date.setHours(date.getHours() + env.timeout) let srcT = date.toISOString() const policyText = { expiration: srcT, //设置该Policy的失效时间 conditions: [ ['content-length-range', 0, 30 * 1024 * 1024], // 设置上传文件的大小限制,30mb ], } const policyBase64 = Base64.encode(JSON.stringify(policyText)) return policyBase64 }, getSignature: function (policyBase64) { const accesskey = env.AccessKeySecret const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, { asBytes: true, }) const signature = Crypto.util.bytesToBase64(bytes) return signature }, }, } .avatar { width: 100rpx; height: 100rpx; line-height: 100rpx; text-align: center; // border-radius: 50%; overflow: hidden; border: 1px solid #625f5f; img { width: 100%; height: 100%; } }
还没有评论,来说两句吧...