引言
随着移动互联网的发展,微信小程序因其便捷性和易用性而广受欢迎。在许多应用场景中,用户需要上传图片来完成特定任务,如头像设置、商品发布等。本文将详细介绍如何利用微信小程序实现图片上传到腾讯云的COS(Cloud Object Storage,对象存储)服务,帮助开发者快速掌握这一技能。
准备工作
-
注册腾讯云账号并开通COS服务
- 登录腾讯云官网并注册账号。
- 在控制台中找到“对象存储COS”服务并开通。
- 创建一个存储桶(Bucket),选择合适的地域和权限配置(建议测试阶段使用公有读私有写权限)。
- 获取存储桶名称(Bucket Name)、地域(Region)和密钥信息(SecretId 和 SecretKey)。
-
创建微信小程序
- 登录微信公众平台,注册并创建一个新的小程序项目。
- 获取小程序的AppID和AppSecret。
-
配置服务器域名
- 在微信小程序管理后台,进入“开发设置”,添加腾讯云COS的API域名,例如:
https://cos.<region>.myqcloud.com
- 在微信小程序管理后台,进入“开发设置”,添加腾讯云COS的API域名,例如:
-
安装腾讯云COS SDK
- 腾讯云提供了官方的JavaScript SDK,支持Node.js和其他环境。由于微信小程序无法直接引入大型库,我们需要使用轻量级的HTTP请求方式来完成上传操作。
或者 其他使用的话直接引入cos-js-sdk-v5.min.js文件也可以,小程序的话下载完记得构建一下
具体实现
方式一:直接使用永久密钥
原理
- 永久密钥:直接使用腾讯云账号的
SecretId
和SecretKey
进行签名。 - 优点:实现简单,适合快速开发和测试。
- 缺点:安全性较低,密钥暴露后可能导致数据泄露,因此不推荐在生产环境中使用。
实现步骤
-
配置永久密钥
- 在小程序前端代码中直接嵌入
SecretId
和SecretKey
。 - 注意:这种方式仅适用于测试环境,生产环境中应避免直接暴露密钥。
- 在小程序前端代码中直接嵌入
-
代码示例
app.js
App({
globalData: {
cosConfig: {
bucket: 'your-bucket-name', // 替换为你的存储桶名称
region: 'ap-guangzhou', // 替换为你的存储桶地域
secretId: 'your-secret-id', // 替换为你的SecretId
secretKey: 'your-secret-key' // 替换为你的SecretKey
}
}
});
index.wxml
<view class="container">
<button bindtap="chooseImage">选择图片</button>
<image wx:if="{{tempFilePath}}" src="{{tempFilePath}}" mode="aspectFit"></image>
</view>
index.js
import COS from "cos-wx-sdk-v5";
const app = getApp();
Page({
data: {
tempFilePath: '', // 图片本地路径
cos: null // COS实例
},
onLoad() {
// 初始化COS实例
this.setData({
cos: new COS({
getAuthorization: (options, callback) => {
const authorization = this.generatePermanentSignature();
callback(authorization);
}
})
});
},
chooseImage() {
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
this.setData({ tempFilePath });
this.uploadFileToCOS(tempFilePath);
}
});
},
uploadFileToCOS(filePath) {
const { cos } = this.data;
const { bucket, region } = app.globalData.cosConfig;
cos.putObject({
Bucket: bucket,
Region: region,
Key: `uploads/${Date.now()}.jpg`, // 文件名,可自定义
FilePath: filePath, // 本地文件路径
onProgress: (progressData) => {
console.log('上传进度:', progressData.percent * 100 + '%');
}
}, (err, data) => {
if (err) {
console.error('上传失败:', err);
wx.showToast({ title: '上传失败', icon: 'none' });
} else {
console.log('上传成功:', data);
wx.showToast({ title: '上传成功', icon: 'success' });
}
});
},
generatePermanentSignature() {
const { secretId, secretKey } = app.globalData.cosConfig;
const now = Math.floor(Date.now() / 1000);
const expiredTime = now + 60; // 签名有效期60秒
const keyTime = `${now};${expiredTime}`;
const signKey = this.hmacSha1(secretKey, keyTime);
const stringToSign = `sha1\n${keyTime}\n`;
const signature = this.hmacSha1(signKey, stringToSign);
return {
TmpSecretId: secretId,
TmpSecretKey: secretKey,
XCosSecurityToken: '',
StartTime: now,
ExpiredTime: expiredTime,
Signature: signature
};
},
hmacSha1(key, message) {
// HMAC-SHA1加密算法(实际开发中建议使用后端生成签名)
const crypto = require('crypto');
return crypto.createHmac('sha1', key).update(message).digest('hex');
}
});
方式二:使用临时密钥(推荐)
原理
- 临时密钥:通过腾讯云STS(安全凭证服务)生成临时的
TmpSecretId
、TmpSecretKey
和SessionToken
。 - 优点:安全性高,密钥具有时效性,即使泄露也不会长期影响系统安全。
- 缺点:需要搭建后端服务来生成临时密钥。
实现步骤
-
后端生成临时密钥
使用腾讯云SDK,在后端调用STS接口生成临时密钥。
index.html和上面一致
index.js
import COS from "cos-wx-sdk-v5";
const app = getApp();
Page({
data: {
tempFilePath: '',
cos: null
},
onLoad() {
this.setData({
cos: new COS({
getAuthorization: (options, callback) => {
wx.request({
url: 'http://localhost:3000/get-temp-keys', // 替换为你的后端接口地址
method: 'GET',
success: (res) => {
const { credentials } = res.data;
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
XCosSecurityToken: credentials.sessionToken,
StartTime: credentials.startTime,
ExpiredTime: credentials.expiredTime
});
},
fail: () => {
console.error('获取临时密钥失败');
}
});
}
})
});
},
chooseImage() {
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
this.setData({ tempFilePath });
this.uploadFileToCOS(tempFilePath);
}
});
},
uploadFileToCOS(filePath) {
const { cos } = this.data;
const { bucket, region } = app.globalData.cosConfig;
cos.putObject({
Bucket: bucket,
Region: region,
Key: `uploads/${Date.now()}.jpg`,
FilePath: filePath,
onProgress: (progressData) => {
console.log('上传进度:', progressData.percent * 100 + '%');
}
}, (err, data) => {
if (err) {
console.error('上传失败:', err);
wx.showToast({ title: '上传失败', icon: 'none' });
} else {
console.log('上传成功:', data);
wx.showToast({ title: '上传成功', icon: 'success' });
}
});
}
});
封装方式
cos.js
import COS from "cos-wx-sdk-v5";
import { getCosAccesstoekn } from "../api/module/cos"; // 获取临时密钥接口
let cos = null;
// 获取临时密钥
const getCosInstance = () => {
return new Promise(async (resolve, reject) => {
const res = await getCosAccesstoekn();
console.log(res, "cos");
if (res.code != 200) {
reject("获取 STS 失败");
return;
}
const { credentials, startTime, expiredTime } = res.data;
cos = new COS({
getAuthorization: (options, callback) => {
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
SecurityToken: credentials.sessionToken,
StartTime: startTime,
ExpiredTime: expiredTime
});
},
});
resolve(cos);
});
}
const uploadFileToCOS = async (filePath, cloudPath) => {
try {
// 获取 COS 实例
const cos = await getCosInstance();
// 调用 putObject 方法上传文件
return new Promise((resolve, reject) => {
cos.putObject(
{
Bucket: "*******", // 替换为你的存储桶名称
Region: "******", // 替换为你的存储桶所在区域
Key: cloudPath, // 文件在 COS 上的路径(包括文件名)
FilePath: filePath, // 本地文件路径(微信小程序中的临时文件路径)
Headers: {
'Content-Disposition': encodeURI('inline')
},
onProgress: function (progressData) {
console.log(JSON.stringify(progressData));
},
},
(err, data) => {
if (err) {
console.error("上传失败:", err);
reject(err);
} else {
console.log("上传成功:", data);
const url = `https://${data.Location}`
resolve(url);
}
}
);
});
} catch (error) {
console.error("获取 COS 实例失败:", error);
throw error;
}
};
export {
uploadFileToCOS
};
index.js
// 上传图片
async uploadImage(filePath) {
wx.showLoading({ title: "上传中..." });
try {
const fileName = `uploads/${Date.now()}-${Math.random().toString(36).substr(2, 5)}.png`;
const cos = await uploadFileToCOS(filePath, fileName);
console.log(cos);
this.setData({ imageUrl: cos });
wx.hideLoading();
} catch (error) {
wx.hideLoading();
console.error("获取 STS 失败", error);
wx.showToast({ title: "获取权限失败", icon: "error" });
}
},
思路
封装的目标
这段代码的主要目标是:
- 抽象化:将复杂的腾讯云COS SDK操作封装成一个简单的函数
uploadFileToCOS
,开发者只需调用这个函数即可完成文件上传。 - 模块化:通过分离临时密钥获取逻辑和文件上传逻辑,提高代码的可维护性和复用性。
- 安全性:使用临时密钥(STS)进行认证,避免直接暴露永久密钥。
核心封装逻辑
(1) 获取临时密钥
- 腾讯云COS要求在上传文件时提供身份认证信息(如
SecretId
、SecretKey
、SessionToken
等)。为了保证安全性,推荐使用临时密钥(STS)代替永久密钥。 - 代码中通过
getCosAccesstoekn
接口从后端获取临时密钥,并将其传递给腾讯云COS SDK。
关键点:
getCosInstance
是一个返回Promise的函数,用于初始化COS实例。- 在
getAuthorization
回调中,将临时密钥(TmpSecretId
、TmpSecretKey
、SecurityToken
)传递给COS SDK。 - 如果临时密钥获取失败(如接口返回非200状态码),会抛出错误并终止流程。
(2) 文件上传逻辑
uploadFileToCOS
是封装好的文件上传函数,接收两个参数:filePath
:本地文件路径(通常是微信小程序选择图片后生成的临时文件路径)。cloudPath
:文件在COS上的存储路径(包括文件名)。
- 函数内部调用
putObject
方法完成文件上传。
关键点:
- 使用
cos.putObject
上传文件,支持进度监听(onProgress
)。 - 上传成功后,返回文件的访问URL(
https://${data.Location}
),方便前端直接使用。 - 如果上传失败,会捕获错误并抛出异常。
总结
- 直接密钥:适合快速开发和测试,但存在安全隐患,不适合生产环境。
- 临时密钥:安全性高,推荐用于生产环境,但需要搭建后端服务。