前言
沉寂了好久的我,又回来了,这次是关于小程序直传阿里云的问题。首先介绍一下项目背景,这个项目需要用到图片上传,正好前段时间对接了浏览器上传阿里云oss,使用stsToken(临时授权)。所以趁机也把小程序的给整了吧,同时也包含图片上传前添加水印文字。
参考文档
微信小程序直传实践 在找了好多文章之后,我发现还是官方文档更靠谱,了解的更透彻。
关于数据和依赖
我公司的oss使用模式:后端创建RAM角色,前端通过接口去获取某个RAM角色的安全令牌(stsToken).这个token是可以过期的,当过期的时候再次去获取一个stsToken,避免了一次获取永久使用的问题。
npm install crypto-js
npm install js-base64
// 在处理policy和signature 需要使用到
原文
-
upload.js
import crypto from 'crypto-js' import { Base64 } from 'js-base64' import { getOss } from '@/utils/utils.js' // 随机字符串 function randomString(num) { const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] let res = '' for (let i = 0; i < num; i++) { let id = Math.ceil(Math.random() * 35) res += chars[id] } return res } // 计算签名。 function computeSignature(accessKeySecret, canonicalString) { return crypto.enc.Base64.stringify(crypto.HmacSHA1(canonicalString, accessKeySecret)) } const date = new Date() date.setHours(date.getHours() + 1) const policyText = { 'expiration': date.toISOString(), // 设置policy过期时间。 'conditions': [ // 限制上传大小。 ['content-length-range', 0, 1024 * 1024 * 1024] ] } export function uploadFile({filePath, extensionName, fileType}) { return new Promise(async(resolve, reject) => { let oss = await getOss() const url = 'https://xxxxxx.xxxx.aliyuncs.com/' const policy = Base64.encode(JSON.stringify(policyText)) // policy必须为base64的string。 const signature = computeSignature(oss.credentials.accessKeySecret, policy) const fileName = oss.path + randomString(6) + +new Date() + extensionName // 路径+随机字符串+当前时间戳+文件格式名 uni.uploadFile({ 'url': url, // #ifdef MP_WEIXIN 'name': 'file', // #endif // #ifdef MP-ALIPAY 'fileType': fileType, // 文件类型支持图片、视频、音频( image / video / audio) 'fileName': 'file', // #endif 'filePath': filePath, // 待上传的文件路径 'formData': { 'key': fileName, // 设置文件上传至OSS后的文件路径 'OSSAccessKeyId': oss.credentials.accessKeyId, signature, policy, success_action_status: '200', // 默认上传成功状态码为204,此处被success_action_status设置为200。 'x-oss-security-token': oss.credentials.securityToken // stsToken }, 'success': (res) => { console.log(res.statusCode) // 默认上传成功状态码为204,此处被success_action_status设置为200。 if (res.statusCode == 200) { console.log(res, '上传成功') resolve({ url: url + fileName }) } else { console.log(res, '上传else') reject(res) } }, 'fail': err => { console.log(err, '上传失败') reject(err) } }) }) } -
图片上传前添加水印
const addWaterMark = (that, ctx, filePath, height, width) => { return new Promise((resolve, reject) => { const padding = 200 const rotate = 35 const getRadian = (degree: number) => degree * Math.PI / 180 ctx.drawImage(filePath, 0, 0, width, height) ctx.translate(width / 2 * Math.tan(getRadian(rotate)), -height / 2 * Math.tan(getRadian(rotate))) ctx.rotate(rotate * Math.PI / 180) // 将图片放到cancas内,宽高为图片大小 ctx.setFontSize(16) ctx.setTextAlign('center') ctx.setFillStyle('#797878') let x = 20 let y = 20 let addingX = true const fillText = '20210305' while (y < height) { ctx.fillText(fillText, x, y) if (x > width) { x = 20 addingX = false } if (addingX) { x += padding } else { y += padding addingX = true } } ctx.draw(false, () => { uni.canvasToTempFilePath({ canvasId: ctx.canvasId, destWidth: width, destHeight: height, success: (res) => { let path = res.tempFilePath resolve(path) }, fail: (err) => { reject(err) } }, that) }) }) }// upload.js export async function uploadWaterMarkImage ({ that, filePath, extensionName, ctx, height, width}) { ... let waterMarkFilePath = await addWaterMark(that, ctx, filePath, height, width) ... }
总结
摸鱼了好久,终于回来了,会继续更文,把自己用的到的东西放到上面来,当做一个经验总结。下一篇关于根据此方法封装的upload组件。