很多场景下,我们需要用户分享活动海报,并且海报上面需要包含用户信息,例如一个携带了用户ID的链接,这时我们就要使用动态生成二维码的技术。
1、准备php环境
我们使用Dockerfile构建一个Apache+PHP的镜像,并且配置安装GD库用于图片处理。
创建项目文件夹:mkdir -p /data/docker/qrcode-demo
创建存放php脚本的目录:mkdir -p /data/docker/qrcode-demo/src
创建存放php依赖的目录:mkdir -p /data/docker/qrcode-demo/src/lib
进入项目文件夹:cd /data/docker/qrcode-demo
创建Dockerfile文件,内容如下:
FROM php:7.0-apache
RUN apt-get update && apt-get install -y \
libfreetype6-dev \
libjpeg62-turbo-dev \
libmcrypt-dev \
libpng-dev \
&& docker-php-ext-install -j$(nproc) iconv mcrypt \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install -j$(nproc) gd
RUN rm -rf /var/www/html/*
RUN mkdir /var/www/html/images && chmod -R 777 /var/www/html/images
RUN mkdir /var/www/html/qrcode && chmod -R 777 /var/www/html/qrcode
COPY src/ /var/www/html/
然后使用以下命令构建镜像:
docker build -t qrcode-demo:1.0 .
首次构建过程可能会等待比较长一段时间,因为构建过程需要安装GD库,默认从国外下载源码进行构建,视网络状况等待10分钟左右即可。
然后执行以下命令运行这个镜像:
docker run -d -rm -p 81:80 -v /data/docker/qrcode-demo/src:/var/www/html qrcode-demo:1.0
为什么要挂载src目录呢?因为刚刚构建时我们的src目录是空的,我们的php代码还没写出来呢,现在挂载了src目录,我们只要在src目录编写代码然后在浏览器访问就能看到效果,方便调试。
2、编写php代码
首先我们准备两个依赖:
① phpqrcode
– 用于生成二维码的库 (传送门)
下载好之后,找到phpqrcode.php
文件复制到本项目src/lib目录下
② resizeImage
– 用于调整图片大小的一个函数
由于这个函数是自己写的,不需要下载。
将以下内容保存到当前项目src/lib/
文件夹,命名为resizeImage.php
<?php
function resizeImage($im,$maxX=100,$maxY=100) {
$width = imagesx($im);
//获取宽度
$height = imagesy($im);
//获取高度
//计算缩放比例
$scale = ($maxX/$width) > ($maxY/$height) ? $maxY / $height : $maxX / $width;
//计算缩放后的尺寸
$sWidth = floor($width*$scale);
$sHeight = floor($height*$scale);
//创建目标图像资源
$nim = imagecreatetruecolor($sWidth,$sHeight);
//等比缩放
imagecopyresampled($nim,$im,0,0,0,0,$sWidth,$sHeight,$width,$height);
//释放图片资源
imagedestroy($im);
return $nim;
}
依赖准备好之后,我们就要开始编写主要代码了。
在项目文件夹的src
目录创建gen.php
文件,内容如下:
<?php
include('./lib/phpqrcode.php');
include('./lib/resizeImage.php');
$IMG_URL = empty($_GET['img'])?null:$_GET['img'];
$LINK = empty($_GET['link'])?null:$_GET['link'];
$POS = empty($_GET['pos'])?null:$_GET['pos'];
$SIZE = empty($_GET['size'])?235:$_GET['size'];
#数据有效性检查 $POS = explode(',',$POS);
if(count($POS) != 2 || !is_numeric($POS[0]) || !is_numeric($POS[1])){
die(10);
}
if(!$IMG_URL || !$LINK){
die(13);
}
//TODO检查文件是否已生成过
function download($url, $path = './images/'){
$filePath = $path.pathinfo($url, PATHINFO_BASENAME);
if(!file_exists($filePath)){
#文件不存在,从网络上下载 $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
$file = curl_exec($ch);
curl_close($ch);
$resource = fopen($filePath, 'a');
fwrite($resource, $file);
fclose($resource);
}
#文件存在,从本地加载
return imagecreatefrompng($filePath);
}
#生成二维码
$qrFilePath = './qrcode/'.md5($LINK).'.png';
$qrImg = null;
if(!file_exists($qrFilePath)){
QRcode::png($LINK,$qrFilePath,QR_ECLEVEL_L,6,3,false);
$qrImg = imagecreatefrompng($qrFilePath);
#调整大小
$qrImg = resizeImage($qrImg,$SIZE,$SIZE);
#存入本地
ImagePng($qrImg,$qrFilePath);
}
if($qrImg == null){
$qrImg = imagecreatefrompng($qrFilePath);
}
#下载并加载背景图
$bgImg = download($IMG_URL);
#合并 imagecopymerge($bgImg,$qrImg,$POS[0],$POS[1],0,0,imagesx($qrImg),imagesy($qrImg),100);
ob_clean();
header('Content-type: image/png');
#输出
imagepng($bgImg);
#释放资源
imagedestroy($bgImg);
imagedestroy($qrImg);
保存之后,放一张png格式的图片到项目文件夹的src目录下,取名为bg.png
然后在浏览器打开:
http://你的IP:81/gen.php?img=http%3a%2f%2flocalhost%2fbg.png&link=https%3a%2f%2fitmx.xyz&pos=0%2c0&size=200
看到上面的url不要慌,我来如果你能看懂上面的php代码,那就不用听我解释了,如果不太明白,那么我大概解释一下:
url中为什么有这么多%号?那是参数经过urlencode之后产生的
img参数:指定一张背景图片,php代码会在这张图片上画一个二维码,上面这条链接编码之前是:http://localhost/bg.png
link参数:生成的二维码的文本内容,上面url中link参数编码前为:https://itmx.xyz
pos参数:将二维码画在背景图的哪个位置,上面url中pos参数编码前为:0,0 ,也就是生成的二维码会在背景图片的左上角。格式:x位置,y位置
size参数:生成的二维码的大小
在测试的过程中,这段代码默认是有缓存二维码的功能的,如果要调试二维码大小,需要注释if(!file_exists($qrFilePath)) 这一个判断,调试完成之后记得解除注释。
如果是用在生产环境中,视情况定时清理二维码缓存。
3、投入使用
代码编写调试完之后,需要重新构建镜像以免挂载目录运行,执行以下命令构建:
docker build -t qrcode-demo:1.0 .
运行镜像:
docker run -d -p 81:80 -rm qrcode-demo:1.0
使用编排文件和nginx-proxy
:
web:
image: qrcode-demo:1.0
expose:
- "80"
environment:
- VIRTUAL_HOST=qrdemo.itmx.xyz
restart: always
跑起来:
docker-compose up -d
现在,你可以为你的项目提供一个二维码合成接口了。
后来发现这个二维码合成的PHP代码存在内存泄漏的问题,就用java实现了这个功能然后集成到项目代码中了