文章目录
前言
由于公司项目后端带宽吃紧,遂要求在前端直传图片至阿里oss,不调用后端接口先传至后端再上传,同时项目需要在某些图片上传后用户看到的图片是自带水印的。关于水印这个问题找了很多帖子都不太适用,于是还是自己封装一个前端直传oss的组件达成需求。
阿里oss上传所需关键的bucketName,accessid和secret,需要替换成自己的,怎么获取的教程其他帖子都有,就偷个懒不贴了。
废话不多说,直接下面贴代码,详细和完整都贴,兄弟们按需求cv(笑)。
一、前端直传oss
在common目录下创建alUpload.js文件
1.传统直传
因为需求该变更,项目中密匙通过后端接口获取,贴传统方式仅供参考
import {
Crypto,
Base64
} from '@/common/bundle.js';//引入下面第二段的工具代码//传统方式直传
export const alUploadImage =function(path, showLoading = true){return new Promise((resolve, reject)=>{
let date = new Date(new Date().getTime()+1000*3600);
let expiration = date.toISOString();//let expiration = date.getFullYear()+'-'+(date.getMonth()+1)+'-'+date.getDate()+'T'+date.getHours()+':'+date.getMinutes()+':00.000Z';
let policyText ={
expiration,//设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了"conditions":[["content-length-range",0,1048576000]// 设置上传文件的大小限制]};
let bucketName ='*********';//你的bucketName
let policyBase64 = Base64.encode(JSON.stringify(policyText))
let accessid ='*********';//你的阿里oss accessid
let accesskey ='*********';//你的阿里oss secret
let host ='http://'+ bucketName +'.oss-cn-chengdu.aliyuncs.com';//上传oss地址,注意地区对不对,我这示例给的是shanghai
let bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey,{
asBytes: true
});
let signature = Crypto.util.bytesToBase64(bytes);//签名// 在阿里云存储路径=>文件地址,地址根据需求生成,下方仅为示例
let fileName ='App/'+'建议使用dayJs'+'/'+'随便你打什么都行,建议使用random函数生成随机字数'+'.'+ path
.split('.').pop();//文件名 注意:相同文件名会覆盖之前的文件if(showLoading) uni.showLoading({
mask: true,
title:'上传中..'})
uni.uploadFile({
url: host,
filePath: path,
fileType:'',
name:'file',
formData:{
name: fileName,
key: fileName,//文件名
policy: policyBase64,// 输入你获取的的policy
OSSAccessKeyId: accessid,// 输入你的AccessKeyId
success_action_status:'200',// 让服务端返回200,不然,默认会返回204
signature,// 输入你获取的的signature},
success:(res)=>{if(showLoading) uni.hideLoading()
res.file = host +'/'+ fileName
resolve(res)},
fail:(res)=>{if(showLoading) uni.hideLoading()reject(false)}})})}
2.后端参数直传oss
因为项目安全问题,项目中密匙通过后端接口获取,获得索需的参数,对应直传里的policy,OSSAccessKeyId等参数,initOssPolicy()为调用后端接口拿到参数
// 获取Oss上传签名
export const uploadOss =(uploadRequestOptions)=>{return new Promise((resolve)=>{
let ossPolicy ={}const file = uploadRequestOptions
initOssPolicy(file.url).then(resp =>{//调用下面的后端接口获取参数
Object.assign(ossPolicy, resp.data.data)alUpload(file, ossPolicy).then(res =>{
console.log("上传后res", res)if(res.errMsg =="uploadFile:ok"){
console.log("resp.data.data--------", resp.data.data)resolve(resp.data.data)}})})})}//上传
export const alUpload =function(file, ossPolicy){return new Promise((resolve, reject)=>{
uni.uploadFile({
url: ossPolicy.host,
fileType:'',
name:'file',
filePath: file.url,
formData:{'name': file.url,'key': ossPolicy.dir + ossPolicy.fileName,'policy': ossPolicy.policy,'signature': ossPolicy.signature,'host': ossPolicy.host,'dir': ossPolicy.dir,'success_action_status':'200','Content-Disposition': ossPolicy.contentDisposition,'x-oss-content-type': ossPolicy.contentType,'OSSAccessKeyId': ossPolicy.accessKeyId,},
success:(res)=>{// console.log("成功****************sesss", res)resolve(res)},
fail:(res)=>{// console.log("失败*************fail", res)reject(false)}})})}
3.调用后端获取所需要的参数
因为项目安全问题,项目中密匙通过后端接口获取,获得索需的参数,对应直传里的policy,OSSAccessKeyId等参数,
//调取后端接口拿到参数
export const initOssPolicy =(fileName)=>{return new Promise((resolve, reject)=>{
uni.request({
url: 'https://s.mga1.cn/saas-api/sys/file/getOssPolicy?fileName=' + fileName,
success:(res)=>{// console.log("回调-----sesss", res)resolve(res)},
fail:(res)=>{// console.log("回调-----fail", res)reject(false)}})})}//接口回调:{accessKeyId:"让你们后端给阿里oss的accessKeyId"
contentDisposition:"inline;filename=bv7OceYfGTwx2fced1094491a9a5c05316c520cc6387_171356.jpeg"
contentType:"image/jpeg"
dir:"20240530/"
fileName:"让你们后端给文件的名字"
fileUrl:"让你们后端给上传后的文件路径"
host:"https://换成你自己的bucketName.oss-cn-chengdu.aliyuncs.com"
policy:"eyJleHBpcmF0aW9uIjoiMjAyNC0wNS0zMFQwOToyMzo1Ni41NjNaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0seyJDb250ZW50LURpc3Bvc2l0aW9uIjoiaW5saW5lO2ZpbGVuYW1lPWJ2N09jZVlmR1R3eDJmY2VkMTA5NDQ5MWE5YTVjMDUzMTZjNTIwY2M2Mzg3XzE3MTM1Ni5qcGVnIn0seyJDb250ZW50LVR5cGUiOiJpbWFnZS9qcGVnIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDAifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsIjIwMjQwNTMwLyJdXX0="
signature:"JRfgVnCd8y5NgeZJtwayK3mSAfM="}
4.业务文件中使用
业务文件demo.vue中使用如下(示例):
<script setup>
import {
uploadOss,
uploadOssWatermark
} from '@/common/alUpload.js'
import dayjs from "dayjs"const paramsStore =paramasUserStore()const{
proxy
}=getCurrentInstance()//选择图片const chooseImg =(){
uni.chooseImage({
success:function(res){
console.log(res.tempFiles[0]);const result = await uploadFilePromise(res.tempFiles[0])
console.log(result.fileUrl);//返回结果对象可以你自己定义}})}//上传文件const uploadFilePromise =(url)=>{return new Promise((resolve, reject)=>{uploadOssWatermark(url).then(res =>{
console.log('上传回调--------------', res.fileUrl)resolve(res);})});};</script>
二、添加水印
利用阿里云 OSS 给图片加水印很简单,只需要在图片 URL 的后面跟上一对水印参数就行,前端访问这个url的时候看到的就是带水印的图片
提示:链接直接浏览器访问即可看到效果
例如:
原始图片:https://oss.mga1.cn/20240530/jo1qy2cLRYCM9cd16bdbf9d9c7786d0b421d65c01d35_175244.jpeg
1.水印上传
直接用下方代码替换上面的uploadOss()即可
export const uploadOssWatermark =(uploadRequestOptions, time, addr, date, man, shopName)=>{return new Promise((resolve)=>{
let ossPolicy ={}const file = uploadRequestOptions
initOssPolicy(file.url).then(resp =>{
Object.assign(ossPolicy, resp.data.data)alUploadWatermark(file, ossPolicy, time, addr, date, man, shopName).then(res =>{
console.log("上传后res", res)if(res.errMsg =="uploadFile:ok"){//添加水印
let mid ="|"
resp.data.data.fileUrl = resp.data.data.fileUrl +"?x-oss-process=image/watermark,text_"+
Base64.encode(time).replace('+','-').replace('/','_').replace('=','')+",g_sw,p_15,color_FFFFFF,size_60,x_100,y_120/watermark,text_"+
Base64.encode(mid)+",g_sw,color_FFFF00,size_80,x_280,y_100/watermark,text_"+
Base64.encode(date).replace('+','-').replace('/','_').replace('=','')+",g_sw,p_15,color_FFFFFF,size_40,x_300,y_150/watermark,text_"+
Base64.encode(shopName).replace('+','-').replace('/','_').replace('=','')+",g_sw,p_15,color_FFFFFF,size_40,x_300,y_100/watermark,text_"+
Base64.encode(addr).replace('+','-').replace('/','_').replace('=','')+",g_sw,p_15,color_FFFFFF,size_40,x_100,y_50/watermark,text_"+
Base64.encode(man).replace('+','-').replace('/','_').replace('=','')+",g_se,p_15,color_FFFFFF,size_40,x_100,y_100/"
console.log("resp.data.data--------", Base64.encode(time),'*******',
Base64.encode(addr).replace('+','-'),)resolve(resp.data.data)}})})})}//2024.7.16优化,最近发现一个小bug,如果特殊字符“+”,“/”,“=”这几个字符在转义后出现多次,原写法的replace只会替换第一个,所以这里优化一下,使用正则来匹配特殊字符,全局替换。替换内容如下://原:replace('+', '-').replace('/', '_').replace('=','')//正则:replace(/[+]/g, '-').replace(/[/]/g, '_').replace(/[=]/g,'')
2.水印参数解释
文字内容需进行Base64编码(Base64编码之前中文字符串的最大字节长度为64个字符。)且需要将文字中的+转换为-,/转换为_,=转换为""
例如:
图片URL+ “?x-oss-process=image/”+“/watermark,text_" +
Base64.encode(你想要添加的水印内容).replace(‘+’, ‘-’).replace(‘/’, ‘_’).replace(‘=’,‘’) +",g_sw,p_15,color_FFFFFF,size_60,x_100,y_120
* text_ 文字水印的文字内容,文字内容需进行Base64编码(Base64编码之前中文字符串的最大字节长度为64个字符。)
* type_ 文字水印的字体,字体名称需进行Base64编码。默认值:wqy-zenhei( 编码后的值为d3F5LXplbmhlaQ)
wqy-zenhei 文泉驿正黑 d3F5LXplbmhlaQ
wqy-microhei 文泉微米黑 d3F5LW1pY3JvaGVp
fangzhengshusong 方正书宋 ZmFuZ3poZW5nc2h1c29uZw
fangzhengkaiti 方正楷体 ZmFuZ3poZW5na2FpdGk
fangzhengheiti 方正黑体 ZmFuZ3poZW5naGVpdGk
fangzhengfangsong 方正仿宋 ZmFuZ3poZW5nZmFuZ3Nvbmc
droidsansfallback DroidSansFallback ZHJvaWRzYW5zZmFsbGJhY2s
* color_ 文字水印的文字颜色,参数值为RGB颜色值。RGB颜色值,例如:000000表示黑色,FFFFFF表示白色。 默认值:000000(黑色)
* size_ 文字水印的文字大小。(0,1000] 默认值:40 单位:px
* t_ 图片水印或水印文字的透明度。[0,100] 默认值:100, 表示透明度100%(不透明)。
* shadow_ 文字水印的阴影透明度。[0,100] 默认值:0,表示没有阴影。
* rotate_ 文字顺时针旋转角度。[0,360] 默认值:0,表示不旋转。
* fill_ 是否将文字水印铺满原图。0、1(1:表示将文字水印铺满原图。0(默认值):表示不将文字水印铺满全图。)
* p_ [1,100] 指定图片水印按照原图的比例进行缩放,取值为缩放的百分比。如设置参数值为10,如果原图为100×100, 则图片水印大小为10×10。当原图变成了200×200,则图片水印大小为20×20。
* g_ 水印在图片中的位置。
nw:左上
north:中上
ne:右上
west:左中
center:中部
east:右中
sw:左下
south:中下
se(默认值):右下
* x_ 指定水印的水平边距, 即距离图片边缘的水平距离。这个参数只有当水印位置是左上、左中、左下、右上、右中、右下才有意义。[0,4096] 默认值:10 单位:像素(px)
* y_ 指定水印的垂直边距,即距离图片边缘的垂直距离, 这个参数只有当水印位置是左上、中上、右上、左下、中下、右下才有意义。 [0,4096] 默认值:10 单位:px
* voffset_ 定水印的中线垂直偏移。当水印位置在左中、中部、右中时,可以指定水印位置根据中线往上或者往下偏移。[-1000,1000] 默认值:0 单位:px
附件bundle.js
附件:以下bundle.js是前置工具类,在common目录下创建bundle.js文件,复制粘接即用
//bundle.js
var base64map ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";// Global Crypto object
var Crypto ={};// Crypto utilities
var util = Crypto.util ={// Bit-wise rotate left
rotl:function(n, b){return(n << b)|(n >>>(32- b));},// Bit-wise rotate right
rotr:function(n, b){return(n <<(32- b))|(n >>> b);},// Swap big-endian to little-endian and vice versa
endian:function(n){// If number given, swap endianif(n.constructor == Number){return util.rotl(n,8)&0x00FF00FF|
util.rotl(n,24)&0xFF00FF00;}// Else, assume array and swap all itemsfor(var i =0; i < n.length; i++)
n[i]= util.endian(n[i]);return n;},// Generate an array of any length of random bytes
randomBytes:function(n){for(var bytes =[]; n >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 < str.length; i++)
bytes.push(str.charCodeAt(i));return bytes;},// Convert a byte array to a string
bytesToString:function(bytes){
var str =[];for(var i =0; i < bytes.length; i++)
str.push(String.fromCharCode(bytes[i]));return str.join("");},// Convert a string to big-endian 32-bit words
stringToWords:function(str){
var words =[];for(var c =0, b =0; c < str.length; c++, b +=8)
words[b >>>5]|= str.charCodeAt(c)<<(24- b %32);return words;},// Convert a byte array to big-endian 32-bits words
bytesToWords:function(bytes){
var words =[];for(var i =0, b =0; i < bytes.length; i++, b +=8)
words[b >>>5]|= bytes[i]<<(24- b %32);return words;},// Convert big-endian 32-bit words to a byte array
wordsToBytes:function(words){
var bytes =[];for(var b =0; b < words.length *32; b +=8)
bytes.push((words[b >>>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 < bytes.length; i++){
hex.push((bytes[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 < hex.length; c +=2)
bytes.push(parseInt(hex.substr(c,2),16));return bytes;},// Convert a byte array to a base-64 string
bytesToBase64:function(bytes){// Use browser-native function if it exists// if (typeof btoa == "function") return btoa(util.bytesToString(bytes));
var base64 =[],
overflow;for(var i =0; i < bytes.length; i++){switch(i %3){case0:
base64.push(base64map.charAt(bytes[i]>>>2));
overflow =(bytes[i]&0x3)<<4;break;case1:
base64.push(base64map.charAt(overflow |(bytes[i]>>>4)));
overflow =(bytes[i]&0xF)<<2;break;case2:
base64.push(base64map.charAt(overflow |(bytes[i]>>>6)));
base64.push(base64map.charAt(bytes[i]&0x3F));
overflow =-1;}}// Encode overflow bits, if there are anyif(overflow != undefined && overflow !=-1)
base64.push(base64map.charAt(overflow));// Add paddingwhile(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 existsif(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 < base64.length; i++){switch(i %4){case1:
bytes.push((base64map.indexOf(base64.charAt(i -1))<<2)|(base64map.indexOf(base64.charAt(i))>>>4));break;case2:
bytes.push(((base64map.indexOf(base64.charAt(i -1))&0xF)<<4)|(base64map.indexOf(base64.charAt(i))>>>2));break;case3:
bytes.push(((base64map.indexOf(base64.charAt(i -1))&0x3)<<6)|(base64map.indexOf(base64.charAt(i))));break;}}return bytes;}};// Crypto mode namespace
Crypto.mode ={};//hmac
var util = Crypto.util;
Crypto.HMAC =function(hasher, message, key, options){// Allow arbitrary length keys
key = key.length > 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; i < hasher._blocksize *4; i++){
okey[i]^=0x5C;
ikey[i]^=0x36;}
var hmacbytes =hasher(util.bytesToString(okey)+hasher(util.bytesToString(ikey)+ message,{ asString: true }),{ asBytes: true });return options && options.asBytes ? hmacbytes :
options && options.asString ? util.bytesToString(hmacbytes):
util.bytesToHex(hmacbytes);};//sha1// 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<<(24- l %32);
m[((l +64>>>9)<<4)+15]= l;for(var i =0; i < m.length; i +=16){
var a = H0,
b = H1,
c = H2,
d = H3,
e = H4;for(var j =0; j <80; j++){if(j <16) w[j]= m[i + j];else{
var n = w[j-3]^ w[j-8]^ w[j-14]^ w[j-16];
w[j]=(n <<1)|(n >>>31);}
var t =((H0 <<5)|(H0 >>>27))+ H4 +(w[j]>>>0)+(
j <20?(H1 & H2 |~H1 & H3)+1518500249:
j <40?(H1 ^ H2 ^ H3)+1859775393:
j <60?(H1 & H2 | H1 & H3 | H2 & H3)-1894007588:(H1 ^ H2 ^ H3)-899497514);
H4 = H3;
H3 = H2;
H2 =(H1 <<30)|(H1 >>>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;//base64
var 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 < 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;}elseif(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 < input.length){
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._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 = Base64._utf8_decode(output);return output;},// private method for UTF-8 encoding
_utf8_encode :function(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);}elseif((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;},// private method for UTF-8 decoding
_utf8_decode :function(utftext){
var string ="";
var i =0;
var c = c1 = c2 =0;while( i < utftext.length ){
c = utftext.charCodeAt(i);if(c <128){
string += String.fromCharCode(c);
i++;}elseif((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;}}
export {Crypto, Base64}
版权归原作者 夕溯流光 所有, 如有侵权,请联系我们删除。