您的当前位置:首页正文

微信小程序直接上传文件到阿里云OSS组件封装

2024-12-01 来源:个人技术集锦

微信小程序直接上传文件到OSS

1. 封装公共方法

// utils/upload/base64.js

var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
    -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
function encode(str) {
    var out, i, len;
    var c1, c2, c3;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        c1 = str.charCodeAt(i++) & 0xff;
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt((c1 & 0x3) << 4);
            out += "==";
            break;
        }
        c2 = str.charCodeAt(i++);
        if (i == len) {
            out += base64EncodeChars.charAt(c1 >> 2);
            out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
            out += base64EncodeChars.charAt((c2 & 0xF) << 2);
            out += "=";
            break;
        }
        c3 = str.charCodeAt(i++);
        out += base64EncodeChars.charAt(c1 >> 2);
        out += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
        out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
        out += base64EncodeChars.charAt(c3 & 0x3F);
    }
    return out;
}
function decode(str) {
    var c1, c2, c3, c4;
    var i, len, out;
    len = str.length;
    i = 0;
    out = "";
    while (i < len) {
        /* c1 */
        do {
            c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        } while (i < len && c1 == -1);
        if (c1 == -1)
            break;
        /* c2 */
        do {
            c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
        } while (i < len && c2 == -1);
        if (c2 == -1)
            break;
        out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
        /* c3 */
        do {
            c3 = str.charCodeAt(i++) & 0xff;
            if (c3 == 61)
                return out;
            c3 = base64DecodeChars[c3];
        } while (i < len && c3 == -1);
        if (c3 == -1)
            break;
        out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
        /* c4 */
        do {
            c4 = str.charCodeAt(i++) & 0xff;
            if (c4 == 61)
                return out;
            c4 = base64DecodeChars[c4];
        } while (i < len && c4 == -1);
        if (c4 == -1)
            break;
        out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
    }
    return out;
}


function utf16to8(str) {
    var out, i, len, c;
    out = "";
    len = str.length;
    for (i = 0; i < len; i++) {
        c = str.charCodeAt(i);
        if ((c >= 0x0001) && (c <= 0x007F)) {
            out += str.charAt(i);
        } else if (c > 0x07FF) {
            out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
            out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
            out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
        } else {
            out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
            out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
        }
    }
    return out;
}
function utf8to16(str) {
    var out, i, len, c;
    var char2, char3;
    out = "";
    len = str.length;
    i = 0;
    while (i < len) {
        c = str.charCodeAt(i++);
        switch (c >> 4) {
            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                // 0xxxxxxx
                out += str.charAt(i - 1);
                break;
            case 12: case 13:
                // 110x xxxx 10xx xxxx
                char2 = str.charCodeAt(i++);
                out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
                break;
            case 14:
                // 1110 xxxx 10xx xxxx 10xx xxxx
                char2 = str.charCodeAt(i++);
                char3 = str.charCodeAt(i++);
                out += String.fromCharCode(((c & 0x0F) << 12) |
                    ((char2 & 0x3F) << 6) |
                    ((char3 & 0x3F) << 0));
                break;
        }
    }
    return out;
}


module.exports = {
    encode: encode,
    decode: decode,
    utf16to8: utf16to8,
    utf8to16: utf8to16
}
// utils/upload/base64.js

const Crypto = {};
(function(){
var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// 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 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 < 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) {
				case 0:
					base64.push(base64map.charAt(bytes[i] >>> 2));
					overflow = (bytes[i] & 0x3) << 4;
					break;
				case 1:
					base64.push(base64map.charAt(overflow | (bytes[i] >>> 4)));
					overflow = (bytes[i] & 0xF) << 2;
					break;
				case 2:
					base64.push(base64map.charAt(overflow | (bytes[i] >>> 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 < base64.length; i++) {
			switch (i % 4) {
				case 1:
					bytes.push((base64map.indexOf(base64.charAt(i - 1)) << 2) |
					           (base64map.indexOf(base64.charAt(i)) >>> 4));
					break;
				case 2:
					bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0xF) << 4) |
					           (base64map.indexOf(base64.charAt(i)) >>> 2));
					break;
				case 3:
					bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & 0x3) << 6) |
					           (base64map.indexOf(base64.charAt(i))));
					break;
			}
		}
		return bytes;
	}
};

// Crypto mode namespace
Crypto.mode = {};
})();
module.exports = Crypto;
// utils/upload/hmac.js

const Crypto = require('./crypto.js');
(function(){
// Shortcut
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);
};
})();

module.exports = Crypto;
// utils/upload/sha1.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 << (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;
})();

module.exports = Crypto;

以上基础库可以使用第三方库来实现。

// utils/upload/uploadFile.js

import { ossConfig } from '../../config/index'  //配置文件
const base64 = require('base64.js');//Base64,hmac,sha1,crypto相关算法
require('hmac.js');
require('sha1.js');
const Crypto = require('crypto.js');

/*
 *上传文件到阿里云oss
 *@param - filePath :图片的本地资源路径
 *@param - dir:表示要传到哪个目录下
 *@param - successc:成功回调
 *@param - failc:失败回调
 */ 
const uploadFile = function (filePath, dir, successc, failc) {
  if (!filePath || filePath.length < 9) {
    wx.showModal({
      title: '图片错误',
      content: '请重试',
      showCancel: false,
    })
    return;
  }
  
  //图片名字 可以自行定义,这里是采用当前的时间戳 + 150内的随机数来给图片命名的
  const aliyunFileKey = dir+ new Date().getTime() + Math.floor(Math.random() * 150) + '.png';
  
  const aliyunServerURL = ossConfig.bucketUrl; //OSS地址,需要https  uploadImageUrl
  const accessid = ossConfig.accessKeyId;
  const policyBase64 = getPolicyBase64();
  const signature = getSignature(policyBase64);//获取签名
  return new Promise((resolve, reject) => {
    wx.uploadFile({
      url: aliyunServerURL,//开发者服务器 url
      filePath: filePath,//要上传文件资源的路径
      name: 'file',//必须填file
      formData: {
        'key': aliyunFileKey,
        'policy': policyBase64,
        'OSSAccessKeyId': accessid,
        'signature': signature,
        'success_action_status': '200',
      },
      success: function (res) {
        if (res.statusCode != 200) {
          failc(new Error('上传错误:' + JSON.stringify(res)))
          return;
        }
        successc(aliyunServerURL+"/"+aliyunFileKey);
        resolve(aliyunServerURL+"/"+aliyunFileKey)
      },
      fail: function (err) {
        err.wxaddinfo = aliyunServerURL;
        failc(err);
        reject(err)
      },
    })
  })
}

const getPolicyBase64 = function () {
  let date = new Date();
  date.setHours(date.getHours() + ossConfig.timeout);
  let srcT = date.toISOString();
  const policyText = {
    "expiration": srcT, //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了 
    "conditions": [
      ["content-length-range", 0, 5 * 1024 * 1024] // 设置上传文件的大小限制,5mb
    ]
  };

  const policyBase64 = base64.encode(JSON.stringify(policyText));
  return policyBase64;
}

const getSignature = function (policyBase64) {
  const accesskey = ossConfig.accessKeySecret;

  const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, {
    asBytes: true
  });
  const signature = Crypto.util.bytesToBase64(bytes);

  return signature;
}

module.exports = uploadFile;

以上文件中用到OSS 配置文件:

// config/index.js

export const ossConfig = {
  bucketUrl: "https://bucker-****-****.oss-cn-shenzhen.aliyuncs.com",
  accessKeyId: "LTAI5t*********Yggx",
  accessKeySecret: "6yH2fdn******2aFrDqbqiPD7",
  regionId: "cn-shenzhen",
  timeout: 80000,
  dirPrefix: "test/headImage/"  // 保存文件路径前缀
}

2. 上传头像

<!--pages/order/order.wxml-->
<view style="margin-top: 100rpx;text-align: center;">上传头像</view>
<button class="avatar-wrapper" open-type="chooseAvatar" bind:chooseavatar="onChooseAvatar">
  <image class="avatar" src="{{ logo ? logo : '' }}"></image>
</button> 
<button type="primary" bindtap="onSubmit">确认上传</button>
// pages/order/order.js
import { ossConfig } from '../../config/index'
const upload = require('../../utils/upload/uploadFile')
Page({
  data: {
    logo: ''
  },
  onShow() {
    this.getTabBar().init()
  },
  async uploadImage(path){ // 上传文件到OSS
    const _self = this;
    wx.showLoading({
      title: '上传中',
      mask: true
    })
    var nowTime = new Date();
    let parseDate = "" + nowTime.getFullYear() + (nowTime.getMonth()+1 < 10 ? ('0'+nowTime.getMonth()+1) : (nowTime.getMonth()+1)) + (nowTime.getDay() < 10 ? ('0'+nowTime.getDay()) : nowTime.getDay())
    return upload(path, ossConfig.dirPrefix + parseDate + '/',
      function (result) {
        _self.setData({
          logo: result
        })
        wx.hideLoading();
      }, function (err) {
        wx.hideLoading();
        wx.showToast({
          title: '上传失败',
          icon: 'none'
        })
      }
    )
  },
  onChooseAvatar(e) { // 选择图片预览
    const { avatarUrl } = e.detail 
    this.setData({
      logo: avatarUrl
    })
  },
  async onSubmit(){
    // 上传图片
    if(this.data.logo.indexOf("//tmp") > -1){
      await this.uploadImage(this.data.logo)
    }
    console.log(this.data.logo)  // OSS 上传文件路径
  }
})
/* pages/order/order.wxss */
.avatar-wrapper {
  padding: 0;
  width: 100rpx !important;
  height: 100rpx !important;
  border-radius: 50%;
  border: none !important;
  background-color: #ccc;
  display: block;
  margin: 50rpx auto;
}
.avatar-wrapper .avatar {
  display: inline;
  width: 100rpx;
  height: 100rpx;
}

实现效果:

头像昵称填写,从基础库 2.21.2 开始支持,当小程序需要让用户完善个人资料时,可以通过微信提供的头像昵称填写能力快速完善。

根据相关法律法规,为确保信息安全,由用户上传的图片、昵称等信息微信侧将进行安全检测,组件从基础库2.24.4版本起,已接入内容安全服务端接口(mediaCheckAsync、msgSecCheck),以减少内容安全风险对开发者的影响。
官方文档:

显示全文