之前用了幾種方式
1.後端生成二維碼需要加密的字符竄,小程序前端利用二維碼組件渲染canvas畫出二維碼,由於支付寶小程序沒有這樣的組件,於是去找微信小程序的解決方案,把微信小程序的二維碼前端組件搬過來用,調試到不報錯了,結果二維碼顯示不出來,原因很難找,宣告失敗!
2.後端生成圖片,並把圖片的地址URL發到小程序前端,前端調用my.downloadFile 成功,但後端卻要生成大量圖片,並且二維碼都是臨時拿來用的,用完就扔,缺點太明顯,後端要產生大量垃圾文件和IO操作,使用碼的客戶端多了,對服務器端有很大壓力
3.後端生成圖片的Base64字符串,小程序前端直接顯示二維碼
我最後使用的是第三種方式
要生成二維碼,首先是使用.net core 版本的QRCode,然後我們的需求是二維碼中間加入一個logo,下面是一個工具類,可實現這個需求
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using QRCoder;
namespace SchoolWebApi.Utility
{
public class RaffQRCode : IQRCode
{
public byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
stream.Position = 0;
stream.Read(bytes, 0, bytes.Length);
stream.Close();
return bytes;
}
/// <summary>
/// 生成二維碼圖片
/// </summary>
/// <param name="logoPath"></param>
/// <param name="content"></param>
/// <param name="pixel">像素大小</param>
/// <returns></returns>
public byte[] GetQRCode(string logoPath, string content, int pixel)
{
var generator = new QRCodeGenerator();
var codeData = generator.CreateQrCode(content, QRCodeGenerator.ECCLevel.M, true);
var qrcode = new QRCode(codeData);
var qrImage = qrcode.GetGraphic(pixel, Color.Black, Color.White, true);
using (MemoryStream ms = new MemoryStream())
{
qrImage.Save(ms, ImageFormat.Png);
using (MemoryStream ms1 = new MemoryStream())
{
//把logo圖片放到二維碼圖片正中心
var newImage = CombinImage(qrImage, logoPath);
newImage.Save(ms1, ImageFormat.Png);
return StreamToBytes(ms1);
}
}
}
/// <summary>
/// 調用此函數後使此兩種圖片合併,類似相冊,有個
/// 背景圖,中間貼自己的目標圖片
/// </summary>
/// <param name="imgBack">粘貼的源圖片</param>
/// <param name="destImg">粘貼的目標圖片</param>
public static Image CombinImage(Image imgBack, string destImg)
{
Image img = Image.FromFile(destImg); //照片圖片
if (img.Height != 65 || img.Width != 65)
{
img = KiResizeImage(img, 65, 65, 0);
}
Graphics g = Graphics.FromImage(imgBack);
g.DrawImage(imgBack, 0, 0, imgBack.Width, imgBack.Height); //g.DrawImage(imgBack, 0, 0, 相框寬, 相框高);
//g.FillRectangle(System.Drawing.Brushes.White, imgBack.Width / 2 - img.Width / 2 - 1, imgBack.Width / 2 - img.Width / 2 - 1,1,1);//相片四周刷一層黑色邊框
//g.DrawImage(img, 照片與相框的左邊距, 照片與相框的上邊距, 照片寬, 照片高);
g.DrawImage(img, imgBack.Width / 2 - img.Width / 2, imgBack.Width / 2 - img.Width / 2, img.Width, img.Height);
GC.Collect();
return imgBack;
}
/// <summary>
/// Resize圖片
/// </summary>
/// <param name="bmp">原始Bitmap</param>
/// <param name="newW">新的寬度</param>
/// <param name="newH">新的高度</param>
/// <param name="Mode">保留着,暫時未用</param>
/// <returns>處理以後的圖片</returns>
public static Image KiResizeImage(Image bmp, int newW, int newH, int Mode)
{
try
{
Image b = new Bitmap(newW, newH);
Graphics g = Graphics.FromImage(b);
// 插值算法的質量
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel);
g.Dispose();
return b;
}
catch
{
return null;
}
}
}
}
上面自定義了一個接口 IQRCode
public interface IQRCode
{
byte[] GetQRCode(string logoPath,string url, int pixel);
}
方便在StartUp中註冊
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IQRCode, RaffQRCode>();
......
然後可以在Controller的構造函數中注入了
private IQRCode _iQRCode;
public GenerateQRCodeController(
IQRCode iQRCode)
{
_iQRCode = iQRCode;
}
接着寫出返回圖片Base64字符串的方法,並在支付寶客戶端調用
/// <summary>
/// 返回二維碼圖片的base64字符竄
/// </summary>
/// <param name="ali_user_id"></param>
/// <returns></returns>
[HttpGet]
[Route("GetGRCodeGenrateImage")]
public ActionResult GetGRCodeGenrateImage(string ali_user_id)
{
try
{
//根據支付寶賬號查詢用戶表
var findUser = _tb_school_user.FindByClause(t => t.ali_user_id == ali_user_id);
if (findUser != null)
{
var qrCodeFullStr = GetQrCodeString(ali_user_id, findUser);
var logoPath = host.WebRootPath + @"\images\logo.jpg";
var byteData = _iQRCode.GetQRCode(logoPath, qrCodeFullStr, 4);
//創建一個文件流
//using (FileStream fs = new FileStream(@"c:\666666666666.png", FileMode.Create))
//{
// //將byte數組寫入文件中
// fs.Write(byteData, 0, byteData.Length);
//}
//處理返回提前臺image圖片
//FileContentResult img = new FileContentResult(byteData, "images/png");
//return img;
return Content(Convert.ToBase64String(byteData));
}
return Content(string.Empty);
}
catch (Exception ex)
{
log.Error("錯誤:" + ex);
return Content("圖片生成出錯");
}
}
支付寶小程序前端的代碼
const app = getApp();
Page({
data: {
src: '',
username: '',
studentid: '',
QRCodeSrc: '',
setInter:'',
},
GetQRCode : function(){
var that = this;
my.httpRequest({
url: app.globalData.apiurl + '/api/GenerateQRCode/GetGRCodeGenrateImage',
method: 'GET',
dataType : 'text',
data: {
ali_user_id: app.globalData.aliuserid
},
header: {
'content-type': 'application/octet-stream',
},
success: function(res) {
var data = res.data
if (res.status == 200) {
that.setData ({
QRCodeSrc: 'data:image/png;base64,' + data, //data 爲接口返回的base64字符串
})
}
},
fail: function(res) {
console.log(JSON.stringify(res));
my.showToast({
type: 'fail',
content: '獲取二維碼失敗',
duration: 1000
});
},
complete: function(res) {
my.hideLoading();
}
})
},
//定時刷新二維碼
refreshQRCode : function(){
var that = this;
//每分鐘刷新
that.data.setInter = setInterval(function(){
that.GetQRCode();
},60000);
},
imageError: function (e) {
console.log('image 發生錯誤', e.detail.errMsg)
},
imageLoad: function (e) {
console.log('image 加載成功', e);
},
clearTimeInterval: function (that) {
var interval = that.data.interval;
clearInterval(interval)
},
onLoad(query) {
var that = this
// 頁面加載
app.getUserInfo().then(
user => {
console.info(user);
//設置頭像
if (user.avatar.length > 0) {
this.setData({src: user.avatar});
}
else {
this.setData({src: '/images/tou.png'});
}
//設置用戶名
if (app.globalData.username) {
this.setData({username: app.globalData.username});
}
else {
this.setData({username: user.nickName});
}
//設置StudentId
if(app.globalData.studentid) {
this.setData({studentid: app.globalData.studentid});
}
//頁面加載獲取二維碼
this.GetQRCode();
//開啓定時刷新獲取二維碼
this.refreshQRCode();
}
);
},
onShow() {
// 頁面顯示
my.onUserCaptureScreen(function() {
my.alert({content: '收到用戶截屏事件'});
});
},
onReady() {
},
onUnload: function () {
var that = this;
//清除計時器 即清除setInter
clearInterval(that.data.setInter)
},
});
重點看GetQRCode 這個方法就可以了,如何在前端接收Base64格式並顯示圖片