微信小程序直传实践
使用的是uview的u-upload组件
1、表单页面
a、页面
<u-upload :fileList="fileList" name="1" multiple :maxCount="3" @delete="deleteFun" @afterRead="afterRead"
:previewFullImage="true"></u-upload>
b、主要js
async afterRead(event) {
let fileArr = event.file;
let fileList = this.fileList.concat(event.file);
this.fileList = fileList; //显示在页面上的图片列表
for (let i = 0; i < fileArr.length; i++) {
let data = await initClient(); //获取需要的上传的参数,文件在后面
const host = 'http://阿里后台配置的桶名.oss-cn-shenzhen.aliyuncs.com';
const signature = data.signature;
const ossAccessKeyId = data.OSSAccessKeyId;
const policy = data.policy;
const securityToken = data.securityToken;
let tempFile = fileArr[i];
let filePath =tempFile.url;
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let month2 = month < 10 ? "0" + month : month;
let date2 = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
let name = "";
if (!tempFile.name) {
let path = tempFile.url;
let indexOf = path.lastIndexOf("/");
name = path.substring(indexOf + 1);
} else {
name = tempFile.name;
}
let indexOf = name.lastIndexOf(".");
let extension = name.substring(indexOf + 1);
const key =
`public/car/${tenantId}/${year}/${month2}/${date2}/${uuidv4()}.${extension}`;
//const key = '/public/car/000000/2023/01/12/'+res.tempFiles[0].name; //多了一根斜杠报错:InvalidObjectName
uni.uploadFile({
url: host, // 开发者服务器的URL。
filePath: filePath, // 待上传文件的文件路径。
name: 'file', // 必须填file。
formData: {
key,//传到服务器的地址,切记千万不要有斜杠
policy,
'success_action_status': '200', //让服务端返回200,不然,默认会返回204
OSSAccessKeyId: ossAccessKeyId,
signature,
'x-oss-security-token': securityToken // 使用STS签名时必传。
},
success: (res2) => {
if (res2.statusCode === 200) {
let link = host + "/" + key;
//调取后台接口
saveAttachAPI({
link: link,
domainUrl: host,
name: key,
originalName: name,
extension: extension,
attachSize: tempFile.size
}).then(([err, attach_data]) => {
if (err) return;
let params = {
attachId: attach_data,
link: link,
originalName:name,
attachCategory:1,
businessType:1
};
this.requestFileList.push(params);//保存需要保存到后台的是数据
})
}
},
fail: err => {
console.log(err);
}
});
}
}
}
ossClient.js
const MpUploadOssHelper = require("./uploadOssHelper.js");
import {
getStsAPI
} from "@/api/api/my.js"
import {
getNowTime
} from "@/utils/utils.js"
export const initClient = async () => {
let value = uni.getStorageSync('ossConfig'); //获取缓存里面储存的oss配置数据
//对比时间和缓存里面是否有值,如果过期,需要重新获取sts参数
if (value && value.expiration > getNowTime()) {
return value;
} else {
let [err, data] = await getStsAPI();
const mpHelper = new MpUploadOssHelper({
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户并授权。
accessKeyId: data.accessKeyId,
accessKeySecret: data.accessKeySecret,
});
// 生成参数。
const params = mpHelper.createUploadParams();
params.securityToken = data.securityToken;
params.expiration = data.expiration;
//保存oss配置参数
uni.setStorage({
key: 'ossConfig',
data: params,
});
return params;
}
}
uploadOssHelper.js(这个是从oss文档里面copy出来的)
const crypto = require("crypto-js");
class MpUploadOssHelper {
constructor(options) {
this.accessKeyId = options.accessKeyId;
this.accessKeySecret = options.accessKeySecret;
// 限制参数的生效时间,单位为小时,默认值为1。
this.timeout = options.timeout || 1;
// 限制上传文件的大小,单位为MB,默认值为10。
this.maxSize = options.maxSize || 10;
}
createUploadParams() {
const policy = this.getPolicyBase64();
const signature = this.signature(policy);
return {
OSSAccessKeyId: this.accessKeyId,
policy: policy,
signature: signature,
};
}
getPolicyBase64() {
let date = new Date();
// 设置policy过期时间。
date.setHours(date.getHours() + this.timeout);
let srcT = date.toISOString();
const policyText = {
expiration: srcT,
conditions: [
// 限制上传文件大小。
["content-length-range", 0, this.maxSize * 1024 * 1024],
],
};
const buffer = Buffer.from(JSON.stringify(policyText));
return buffer.toString("base64");
}
signature(policy) {
return crypto.enc.Base64.stringify(
crypto.HmacSHA1(policy, this.accessKeySecret)
);
}
}
module.exports = MpUploadOssHelper;
利用组件: flutter_oss_aliyun
flutter_oss_aliyun
页面初始化的时候,调用此方法
static initOss() async {
String authorization =
"${ProjectConfig.clientId}:${ProjectConfig.clientSecret}";
String base64Authorization = base64Encode(utf8.encode(authorization));
var prefs = await SharedPreferences.getInstance();
Client2.init(
stsUrl:
'${ProjectConfig.baseUrl}/blade-resource/oss/endpoint/get-acs-param-ali',
stsHeader: {
'Authorization': 'Basic ${base64Authorization}',
'Blade-Auth': 'bearer ${prefs.getString("accessToken")}',
'Content-Type': 'application/json'
},
ossEndpoint: 'oss-cn-shenzhen.aliyuncs.com',
bucketName: 'bucketName');
}
上传文件调用此方法:
XFile? image = await _picker.pickImage(source: ImageSource.gallery,imageQuality: 40);
//获取文件
var file = await image?.readAsBytes();
//获取文件大小
int? size =
file?.lengthInBytes;
String uploadPath='upload/${ProjectConfig.tenantId}/${FunUtils.getNowDate()}/${uuid.v1()}.$extension';
await Client2()
.putObject(
file!,//字节文件
uploadPath,//上传到服务器上的地址
);
main.js
//oss配置
Vue.prototype.aliConfig = {
stsUrl:'/api/blade-resource/oss/endpoint/get-acs-param-ali', // 获取sts服务
region:'oss-cn-shenzhen',
bucket:'bucketName',
tenantId:"000000",//租户id
system:"car",//系统类型
rsUrl:"/api/blade-resource/attach/save" //保存到后台的地址
};
修改avue上传模块源码
avue/packages/element-ui/upload/index.vue
修改源码对比
<template>
<div :class="b()"
v-loading.lock="loading">
<el-upload :class="b({'list':listType=='picture-img','upload':disabled})"
@click.native="handleClick"
:action="action"
:on-remove="handleRemove"
:accept="acceptList"
:before-remove="beforeRemove"
:multiple="multiple"
:on-preview="handlePreview"
:limit="limit"
:http-request="httpRequest"
:drag="drag"
:readonly="readonly"
:show-file-list="isPictureImg?false:showFileList"
:list-type="listType"
:on-change="handleFileChange"
:on-exceed="handleExceed"
:disabled="disabled"
:file-list="fileList">
<template v-if="listType=='picture-card'">
<i class="el-icon-plus"></i>
</template>
<template v-else-if="listType=='picture-img'">
<el-image v-if="imgUrl"
:src="imgUrl"
:preview-src-list="[imgUrl]"
v-bind="allParams"
@mouseover="menu=true"
:class="b('avatar')"></el-image>
<i v-else
class="el-icon-plus"
:class="b('icon')"></i>
<div class="el-upload-list__item-actions"
:class="b('menu')"
v-if="menu"
@mouseover="menu=true"
@mouseout="menu=false"
@click.stop="()=>{return false}">
<i class="el-icon-zoom-in"
@click.stop="handlePreview({url:imgUrl})"></i>
<i class="el-icon-delete"
v-if="!disabled"
@click.stop="handleDelete(imgUrl)"></i>
</div>
</template>
<template v-else-if="drag">
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
</template>
<template v-else>
<el-button size="small"
type="primary">点击上传</el-button>
</template>
<div slot="tip"
class="el-upload__tip">{{tip}}</div>
</el-upload>
<el-dialog append-to-body
:class="b('dialog')"
top="0"
:modal-append-to-body="true"
:visible.sync="dialogVisible">
<img v-if="typeList.img.test(dialogUrl)"
:src="dialogUrl"
style="max-width:100%"
alt>
<video v-else-if="typeList.video.test(dialogUrl)"
controls="controls"
style="max-width:100%"
:src="dialogUrl"></video>
</el-dialog>
</div>
</template>
<script>
import create from "core/create";
import props from "../../core/common/props.js";
import event from "../../core/common/event.js";
import { getObjValue } from "utils/util";
import { detailImg } from "plugin/canvas/";
import { getToken } from "plugin/qiniu/";
import { getClient } from "plugin/ali/";
import packages from "core/packages";
export default create({
name: "upload",
mixins: [props(), event()],
data () {
return {
menu: false,
loading: false,
typeList: {
img: /(\.(gif|jpg|jpeg|png|GIF|JPG|PNG))|(data:image)/,
video: /\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|ogg|mp4)/
},
dialogUrl: "",
dialogType: true,
dialogVisible: false,
text: [],
file: {}
};
},
props: {
data: {
type: Object,
default: () => {
return {}
}
},
preview: {
type: Object,
default: () => { }
},
value: {},
onRemove: Function,
showFileList: {
type: Boolean,
default: true
},
oss: {
type: String
},
limit: {
type: Number,
default: 10
},
headers: {
type: Object,
default: () => {
return {}
}
},
accept: {
type: [String, Array],
default: ""
},
canvasOption: {
type: Object,
default: () => {
return {};
}
},
fileSize: {
type: Number
},
drag: {
type: Boolean,
default: false
},
loadText: {
type: String,
default: "文件上传中,请稍等"
},
action: {
type: String,
default: ""
},
uploadBefore: Function,
uploadAfter: Function,
uploadDelete: Function,
uploadPreview: Function,
uploadError: Function
},
computed: {
acceptList () {
if (Array.isArray(this.accept)) {
return this.accept.join(',')
}
return this.accept
},
homeUrl () {
return this.propsHttp.home || ''
},
allParams () {
if (this.typeList.video.test(this.imgUrl)) {
return Object.assign({
is: 'video'
}, this.params)
}
return this.params
},
fileName () {
return this.propsHttp.fileName || 'file'
},
isAliOss () {
return this.oss === "ali";
},
isQiniuOss () {
return this.oss === "qiniu";
},
isPictureImg () {
return this.listType === "picture-img";
},
//单个头像图片
imgUrl () {
if (!this.validatenull(this.text)) {
return this.homeUrl + this.text[0];
}
},
fileList () {
let list = [];
const flag = this.isArray || this.isString;
(this.text || []).forEach((ele, index) => {
if (ele) {
let name;
//处理单个url链接取最后为label
if (flag) {
let i = ele.lastIndexOf('/');
name = ele.substring(i + 1);
}
list.push({
uid: index + '',
status: 'done',
name: flag ? name : ele[this.labelKey],
url: this.homeUrl + (flag ? ele : ele[this.valueKey])
});
}
});
return list;
}
},
created () { },
watch: {},
mounted () { },
methods: {
handleClick () {
if (typeof this.click === "function")
this.click({ value: this.text, column: this.column });
},
handleFileChange (file, fileList) {
fileList.splice(fileList.length - 1, 1);
if (typeof this.change === "function")
this.change({ value: this.text, column: this.column });
},
handleSuccess (file) {
if (this.isArray || this.isString) {
this.text.push(file[this.urlKey]);
} else if (this.isPictureImg) {
this.text.splice(0, 1, file[this.urlKey])
} else {
let obj = {};
obj[this.labelKey] = file[this.nameKey];
obj[this.valueKey] = file[this.urlKey];
this.text.push(obj);
}
this.$message.success("上传成功");
},
handleRemove (file, fileList) {
this.onRemove && this.onRemove(file, fileList);
this.delete(file);
this.$message.success("删除成功");
},
handleError (error) {
if (typeof this.uploadError === "function") {
this.uploadError(error, this.column)
}
},
delete (file) {
if (this.isArray || this.isString) {
(this.text || []).forEach((ele, index) => {
if (ele === file.url) this.text.splice(index, 1);
});
} else {
(this.text || []).forEach((ele, index) => {
if (ele[this.valueKey] === file.url) this.text.splice(index, 1);
});
}
},
show (data) {
this.loading = false;
this.handleSuccess(data);
},
hide (msg) {
this.loading = false;
this.handleError(msg);
},
httpRequest (config) {
this.loading = true;
let file = config.file;
const accept = file.type;
const fileSize = file.size;
this.file = config.file;
let acceptList = this.acceptList;
if (!Array.isArray(acceptList) && !this.validatenull(acceptList)) {
acceptList = acceptList.split(',')
}
if (!this.validatenull(acceptList) && !acceptList.includes(accept)) {
this.hide("文件类型不符合");
return;
}
if (!this.validatenull(fileSize) && fileSize > this.fileSize) {
this.hide("文件太大不符合");
return;
}
const headers = Object.assign(this.headers, { "Content-Type": "multipart/form-data" });
//oss配置属性
let oss_config = {};
let client = {};
let param = new FormData();
//附加属性
for (let o in this.data) {
param.append(o, this.data[o]);
}
const done = () => {
let url = this.action;
const callack = async (newFile) => {
const uploadfile = newFile || file;
param.append(this.fileName, uploadfile);
//七牛云oss存储
if (this.isQiniuOss) {
if (!window.CryptoJS) {
packages.logs("CryptoJS");
this.hide();
return;
}
oss_config = this.$AVUE.qiniu;
const token = getToken(oss_config.AK, oss_config.SK, {
scope: oss_config.scope,
deadline: new Date().getTime() + oss_config.deadline * 3600
});
param.append("token", token);
url = oss_config.bucket
} else if (this.isAliOss) {
if (!window.OSS) {
packages.logs("AliOSS");
this.hide();
return;
}
// oss_config = this.$AVUE.ali;
// "获取当前时间2023-01-12 20:36:55与this.$AVUE.aliExpiration比较"
if( this.$AVUE.ali.accessKeyId == "" || (this.$AVUE.aliExpiration&&new Date()>new Date(this.$AVUE.aliExpiration.replace(/\-/g, "/")))){
// let res = this.$axios.get(this.aliConfig.stsUrl);
// console.log('res: ', res);
await this.$axios.get(this.aliConfig.stsUrl).then(res=>{
let data = res.data.data;
let ali = {
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
region: this.aliConfig.region,
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
accessKeyId: data.accessKeyId,
accessKeySecret: data.accessKeySecret,
// 从STS服务获取的安全令牌(SecurityToken)。
stsToken: data.securityToken,
// 填写Bucket名称。
bucket: this.aliConfig.bucket
}
this.$AVUE.ali = ali;
this.$AVUE.aliExpiration = data.expiration;
})
}
oss_config = this.$AVUE.ali;
console.log('oss_config: ', oss_config);
client = getClient(oss_config);
}
(() => {
if (this.isAliOss) {
let date = new Date();
return client.put("/public/"+this.aliConfig.system+"/"+this.aliConfig.tenantId+"/"+date.getFullYear()+"/"+date.getMonth()+"/"+date.getDate()+"/"+uploadfile.name, uploadfile);
// return client.put(uploadfile.name, uploadfile);
} else {
if (!window.axios) {
packages.logs('axios');
return Promise.reject()
}
return this.$axios.post(url, param, { headers });
}
})()
.then(async res => {
let list = {};
if (this.isQiniuOss) {
res.data.key = oss_config.url + res.data.key;
}
if (this.isAliOss) {
let reg2 = new RegExp("/"+res.name);
let domainUrl = res.url.replace(reg2,"");
let params={
link:res.url,
domainUrl:domainUrl,
name:res.name,
originalName:uploadfile.name,
extension:(res.name).split(".")[1],
attachSize:uploadfile.size
};
await this.$axios.post(this.aliConfig.rsUrl,params).then(res1=>{
list = getObjValue(Object.assign({},res,{attachId:res1.data.data}), this.resKey, "object");
});
} else {
list = getObjValue(res.data, this.resKey, "object");
}
if (typeof this.uploadAfter === "function")
this.uploadAfter(
list,
() => {
this.show(list);
},
() => {
this.loading = false;
},
this.column
);
else this.show(list);
})
.catch(error => {
if (typeof this.uploadAfter === "function")
this.uploadAfter(error, this.hide, () => {
this.loading = false;
}, this.column);
else this.hide(error);
});
};
if (typeof this.uploadBefore === "function")
this.uploadBefore(this.file, callack, () => {
this.loading = false;
}, this.column);
else callack();
};
//是否开启水印
if (!this.validatenull(this.canvasOption)) {
detailImg(file, this.canvasOption).then(res => {
file = res;
done();
});
} else {
done();
}
},
handleExceed (files, fileList) {
this.$message.warning(
`当前限制选择 ${this.limit} 个文件,本次选择了 ${
files.length
} 个文件,共上传了 ${files.length + fileList.length} 个文件`
);
},
handlePreview (file) {
const callback = () => {
//判断是否为图片
this.dialogUrl = file.url;
if (this.typeList.img.test(file.url)) {
this.dialogVisible = true;
return;
} else if (this.typeList.video.test(file.url)) {
this.dialogVisible = true;
}
}
if (typeof this.uploadPreview === "function") {
this.uploadPreview(file, this.column, callback);
} else {
callback();
}
},
handleDelete (file) {
this.beforeRemove(file).then(() => {
this.text = [];
}).catch(() => {
});
},
beforeRemove (file) {
if (typeof this.uploadDelete === "function") {
return this.uploadDelete(this.column, file);
} else {
return this.$confirm(`是否确定移除该选项?`);
}
}
}
});
</script>