taro + react hooks + oss小程序上传
最近在做一个新项目,用到了taro + react hooks + oss小程序上传。今天就来分享一下其中的技术,目前来看,taro+ react hooks做的小程序很多框架不兼容,尤其是UI框架,能够打包成功的很少。试了只有@nutui/nutui-react-taro 和 taro-ui@3.1.0-beta.2在小程序执行时打包成功,当然了,小程序只需要在微信开发者工具上传即可,不需要打包,之前在这里的纠结的原因是习惯了build,尴尬了。
1、按照taro官方文档构建一个新项目,config配置添加别名配置如下:
alias: {
'@': path.resolve(__dirname, '..', 'src'),
'@/images': path.resolve(__dirname, '..', 'src/images'),
'@/assets': path.resolve(__dirname, '..', 'src/assets'),
'@/components': path.resolve(__dirname, '..', 'src/components'),
//'@/constants': path.resolve(__dirname, '..', 'src/constants'),
//'@/reducers': path.resolve(__dirname, '..', 'src/reducers'),
'@/common': path.resolve(__dirname, '..', 'src/common'),
'@/utils': path.resolve(__dirname, '..', 'src/utils')
},
2、把需要调用的api封装在src/api
import { toQueryString }from '@/utils/filter'
import service from '@/utils/request';
//特殊请求加上这段编码
const otherHeader = {'Content-Type':'application/x-www-form-urlencoded', 'Accept':'*/*' }
//获取oss的信息
export const ossGetAccessUrl = (params, headers) => {
return service({
method:'GET',
url:'/oss/token',
data: params,
headers: headers
})
}
3、把全局主题色封装在common/theme.scss下
@charset "UTF-8";
//默认全局背景主题色!default
$default-background:#F6F6F7;
//默认字体、按钮背景主题色
$default-primary:#52A86A;
4、把全局组件封装在components下
5、把需要过滤的封装在utils/filter下
// 将一个对象转成QueryString
export const toQueryString = (obj) => {
if (!obj)return "";
return cleanArray(
Object.keys(obj).map(key => {
if (obj[key] ===undefined)return "";
return encodeURIComponent(key) +"=" +encodeURIComponent(obj[key]);
})
).join("&");
}
6、把请求拦截封装在utils/request
import Taro from '@tarojs/taro'
const baseURL ='https://xxx.xxx.com/'
let token ='xxxxxxxx'
let flag =true;
export const service = (parmas) => {
parmas.headers['token'] = token;
let result =new Promise((resolve, reject) => {
Taro.request({
url: baseURL + parmas.url,
data: parmas.parmas,
method:parmas.method ,
header: {
...{
'content-type':'application/json' // 默认值
}, ...parmas.headers
},
success:function (response) {
if (response.statusCode && response.statusCode !==200) {
if (flag) {
Taro.showToast({
title:JSON.stringify(response.data.message),
icon:'none'
})
flag =false;
setTimeout(() => {
flag =true;
}, 1000)
}
}
if (response.statusCode ===200) {
resolve(response.data)
}else {
reject();
}
},
fail(e:any) {
let message ="";
switch (e.status) {
case 400:
message ="请求错误";
break;
case 401: {
message ="未授权,请登录";
break;
}
case 403:
message ="没有权限,拒绝访问";
break;
case 404:
message =`请求地址出错`;
break;
case 500:
message ="服务器内部错误";
break;
case 501:
message ="服务未实现";
break;
case 502:
message ="网关错误";
break;
case 503:
message ="服务不可用";
break;
case 504:
message ="网关超时";
break;
case 505:
message ="HTTP版本不受支持";
break;
default:
break;
}
if (flag) {
Taro.showToast({
title: message,
icon:'none'
})
flag =false;
setTimeout(() => {
flag =true;
}, 1000)
}
reject(e)
}
})
})
return result;
}
下面来讲封装小程序上传图片到oss,因为小程序上传的图片与h5或web端不一样,没有file的概念,就需要看阿里云文档踩坑
1、封装stsToken,需要通过加密等方式得到signature,policy,x-oss-security-token
import cryptofrom 'crypto-js';
import {Base64}from 'js-base64';
// 计算签名。
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 const getFormDataParams =async (credentials) => {
const policy = Base64.encode(JSON.stringify(policyText))// policy必须为base64的string。
const signature =computeSignature(credentials.AccessKeySecret, policy)
const formData = {
OSSAccessKeyId: credentials.AccessKeyId,
signature,
policy,
'x-oss-security-token': credentials.SecurityToken
}
return formData
}
2、封装Taro.uploadFile上传方法,传入host、signature、OSSAccessKeyId、policy、key、filePath
import Taro from "@tarojs/taro";
export const wxUpload = (data) => {
const host = data.host;
const signature = data.signature;
const ossAccessKeyId = data.OSSAccessKeyId;
const policy = data.policy;
const key = data.key;
const securityToken = data['x-oss-security-token'];
const filePath = data.filePath; // 待上传文件的文件路径。
let result =new Promise((resolve, reject) => {
Taro.uploadFile({
url: host, // 开发者服务器的URL。
filePath: filePath,
name:'file', // 必须填file。
formData: {
name: filePath,
key,
policy,
OSSAccessKeyId: ossAccessKeyId,
signature,
success_action_status:"200",
'x-oss-security-token': securityToken// 使用STS签名时必传。
},
success: (res) => {
console.log(res)
if (res.statusCode ===200) {
Taro.showToast({title:'上传成功', icon:'none'})
resolve(host +'/' + key);
}
},
fail: err => {
reject(null)
Taro.showToast({title:'上传失败', icon:'none'})
return null;
}
});
})
return result;
}
3、封装osstoken获取以及图片上传调用,在选完图片后会调用一次ossGetAccessUrl,可优化为进入首页调用,放在dva中缓存起来ossGetAccessUrl,考虑到oss的token可能会失效,所以我这里每次上传都调用一次
import {randomString, filterUTCNo}from './filter'
import OSS from 'ali-oss';
import Taro from "@tarojs/taro";
import {getFormDataParams}from './sts'
import {wxUpload}from './wxUpload'
import {ossGetAccessUrl}from '@/api/home'
class MyUploadAdapterAll {
constructor(params) {
// 要在上载期间使用的文件加载器实例
this.file = params.file
this.ossGetAccessUrlData = {}
}
async getAccessUrl() {
if (!this.file) {
return
}
try {
Taro.showLoading()
let res =await ossGetAccessUrl({}, {})
Taro.hideLoading()
if (res.code ===200) {
let dataParams:any =await getFormDataParams({
AccessKeySecret: res.data.accessKeySecret,
AccessKeyId: res.data.accessKeyId,
SecurityToken: res.data.securityToken,
})
dataParams.key =`${filterUTCNo(new Date())}/${randomString(8)}_${Date.now()}.${this.file.split('.')[this.file.split('.').length -1]}`
dataParams.host ='https://' + res.data.bucket +'.' + res.data.endPoint.split('//')[1];
dataParams.filePath =this.file;
console.log(dataParams)
return await wxUpload(dataParams);
}
}catch (e) {
console.log(e)
}
}
}
export default MyUploadAdapterAll
4、在页面中引入调用
import MyUploadAdapterAllfrom "@/utils/uploadAll";
Taro.chooseImage({
count:1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有,在H5浏览器端支持使用 `user` 和 `environment`分别指定为前后摄像头
success:async (res) => {
console.log(res)
const myUploadAdapter =new MyUploadAdapterAll({
dir:'dev',
file: res.tempFilePaths[0]
})
let url:string =await myUploadAdapter.getAccessUrl()
if (url) {
console.log(url)
}
}
})