图片访问实时处理的实现(nodejs和php)

本文介绍了如何通过Nginx+Node.js(express)和Apache+PHP实现图片访问实时处理。通过URL重写,结合图片处理库GraphicsMagick或ImageMagick,实现实时缩放、模糊、水印等功能,并缓存处理后的图片。Apache配置涉及开启rewrite模块和编写.htaccess文件,功能实现与Node.js版相似。

我在访问时光网、网易云音乐等网站时,发现将它们页面中的一些图片URL修改一下就可以得到不同尺寸的图片,于是思考了其实现方案,我的思路是:URL Rewrite + 实时处理 + 缓存,对用户请求的URL进行重写,然后利用图片处理类库对图片进行处理,接着缓存该尺寸图片并输出到浏览器。使用PHP和Node.js实现了一遍,基本达到了需要的效果。

1、Nginx+Node.js(express)实现

URL重写

这里Nginx主要是做一个URL重写和反向代理的功能,配置如下所示:

location ~ /upload/{
    if ($request_uri ~* ^/upload/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$) {
        set $src $1;
        set $w $2;
        set $h $3;
        set $t $4;
        rewrite . /resize?src=$src&w=$w&h=$h&type=$t break;
    }
    proxy_pass        http://127.0.0.1:3000;
}

这里说明一下:Nginx监听本地的80端口,Node.js监听的是3000端口。当用户访问类似http://127.0.0.1/upload/147332819224704_400x300.jpg的地址时,便会被代理到http://127.0.0.1:3000/resize?src=147332819224704&w=400&h=300&type=jpg访问,看起来像是访问一张图片,其实不然。

图片实时处理

我们在Node.js中实时处理图片,进行缩放、模糊、水印等操作,之后将其缓存起来并输出到浏览器。Node.js自身API并不擅长图片的处理,我们可以借助第三方类库来实现,这里推荐GraphicsMagick或ImageMagick,使用它们之前先安装gm模块:       

npm install gm --save

 接着便可以使用GraphicsMagick了,该模块的API可以参考GM模块API介绍。图片处理的实现如下:

app.get('/resize',function(req,res){
	var src = req.query.src,
		width = req.query.w,
		height = req.query.h,
		type = req.query.type;
	var imgFile = uploadDir+src+'.'+type;
	var notFound = '不好意思,该图片不存在或已被删除!';
	var thumb = getThumbImg(src,width,height,type);
	if(isFileExists(imgFile)){
		if(isFileExists(thumb)){
			res.type(type).sendFile(__dirname+'/'+thumb);
		}else{
			imgResize(imgFile,thumb,width,height,type,res);
		}
	}else{
		res.status(404).send(notFound);
	}
});
function imgResize(f,th,w,h,t,r){
	var imgSize = sizeOf(f);
	if(!w||!h||w>=imgSize.width||h>=imgSize.height){
		r.type(t).sendFile(__dirname+'/'+f);
	}else{
		imageMagick(f)
			.resize(w,h,'!')
			.stream(function(err, stdout, stderr) {
				if (err) {
					console.log(err);
					res.end();
				}
				var ws = fs.createWriteStream(th);
				stdout.pipe(ws);
				r.type(t);
				stdout.pipe(r);
			});
	}
}
function isFileExists(filePath){
	var bool = !0;
	try{
		fs.accessSync(filePath,fs.constants.F_OK);
	}catch(err){
		bool = !1;
	}
	return bool;
}

如上代码所示,当用户访问http://127.0.0.1/upload/147332819224704_400x300.jpg时,如果147332819224704这张图片存在,且400x300这个尺寸也存在,则直接输出这张图片,如不存在,则生成一张该尺寸的图片保存并输出到浏览器。如果提供的尺寸超出了图片的原始尺寸,则直接输出原图。我们不仅可以修改尺寸,也可以进行模糊、打水印等操作,这里就不多介绍了。

如果不用Nginx反向代理也是可以的,使用express的正则路由实现,如下所示:

app.get(/^\/upload\/(.+)_(\d+)x(\d+)\.(jpg|png|gif)$/,function(req,res){
	var src = RegExp.$1,
		width = RegExp.$2,
		height = RegExp.$3,
		type = RegExp.$4;
	var imgFile = uploadDir+src+'.'+type;
	var notFound = '不好意思,该图片不存在或已被删除!';
	var thumb = getThumbImg(src,width,height,type);
	if(isFileExists(imgFile)){
		if(isFileExists(thumb)){
			res.type(type).sendFile(__dirname+'/'+thumb);
		}else{
			imgResize(imgFile,thumb,width,height,type,res);
		}
	}else{
		res.status(404).send(notFound);
	}
});

2、Apache+PHP实现

首先得搭建windows下php开发环境,我本人用的是apache2+php5.6,具体的搭建步骤网上一大堆,便不再累述。

开启apache rewrite功能

首先我们得开启Apache rewrite模块功能,去掉配置文件http.conf中LoadModule rewrite_module modules/mod_rewrite.so前面的注释,然后设置Directory块下AllowOverride All,可能有多处,接着重启Apache服务。

配置.htaccess文件

在DocumentRoot目录下,新建.htaccess文件,如果创建不了,可以先创建一个文本,然后另存为,在弹出的对话框文件名处填写".htaccess"即可。之后,编写URL重写规则,如下所示:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^upload/(.+)_([0-9]+)x([0-9]+)\.(jpg|png|gif)$ resize.php?src=$1&w=$2&h=$3&type=$4 [L]

将类似http://127.0.0.1/upload/147332819224704_400x300.jpg地址重写为http://127.0.0.1/resize.php/src=147332819224704&w=400&h=300&type=jpg

功能实现

接下来便是功能的实现,逻辑和nodejs版逻辑一致,代码如下:

function getThumbImg($src,$w,$h,$type)
{
	global $thumbs;
	return $_SERVER['DOCUMENT_ROOT'].$thumbs.$src.'_'.$w.'_'.$h.'.'.$type;
}
function imgResize($f,$th,$w,$h,$t)
{
	$imagick = new Imagick();
	$imagick->readImage($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
	$width = $imagick->getImageWidth();
	$height = $imagick->getImageHeight();
	if(!$w||!$h||$w>=$width||$h>=$height){
		header('Content-Type:image/'.$t);
		echo file_get_contents($_SERVER['DOCUMENT_ROOT'].'\\'.$f);
	}else{
		$imagick->stripImage();
		$imagick->cropThumbnailImage($w, $h);
		$imagick->writeImage($th);
		header('Content-Type:image/'.$t);
		echo $imagick->getImageBlob();
		$imagick->clear();
		$imagick->destroy();
	}
}
$uploadDir = "uploads/images/";
$thumbs = "uploads/thumbs/";
$src = $_GET['src'];
$width = $_GET['w'];
$height = $_GET['h'];
$type = $_GET['type'];
$imgFile = $uploadDir.$src.'.'.$type;
$notFound = '不好意思,该图片不存在或已被删除!';
$thumb = getThumbImg($src,$width,$height,$type);
if(file_exists($imgFile)){
	if(file_exists($thumb)){
		header('Content-Type:image/'.$type);
		echo file_get_contents($thumb);
	}else{
		imgResize($imgFile,$thumb,$width,$height,$type);
	}
}else{
	header("HTTP/1.0 404 Not Found");
	header("status: 404");
	echo $notFound;
}

至此,图片访问实时处理也就完成了。其实大部分图片服务器都需要这样的功能,而不是事先生成好几套尺寸的图片。

<think>嗯,用户想了解如何使用Node.js实现微信支付的Native支付功能。首先,我需要回忆一下微信支付的流程,特别是Native支付的部分。Native支付适用于PC网站,用户扫描二维码后完成支付。所以,我需要考虑整个流程,包括生成订单、获取二维码、处理回调通知等步骤。 首先,用户可能需要了解准备工作,比如申请商户号、设置API密钥等。这部分很重要,因为后续的接口调用都需要这些信息。然后,生成预支付订单,调用统一下单接口,这里需要用到的参数比如appid、mch_id、nonce_str、sign_type、body、out_trade_no、total_fee、spbill_create_ip、notify_url、trade_type等。需要注意的是,Native支付的trade_type是NATIVE,product_id可能也需要,但根据微信文档,可能需要确认是否需要这个参数。 接下来,生成签名。微信支付要求对参数进行签名,使用HMAC-SHA256或者MD5,现在可能默认是HMAC-SHA256。生成签名的步骤是将参数按ASCII码排序,拼接成字符串,加上key,然后进行加密。这可能会容易出错,比如参数顺序不对或者拼接错误,需要特别注意。 然后,调用统一下单接口,获取返回的code_url,这个就是生成二维码的URL。用户需要用这个URL生成二维码图片,供用户扫描支付。生成二维码可以使用第三方库,比如qrcode,这个在Node.js中比较常用。 处理异步通知也是关键的一步。微信支付成功后,会向notify_url发送POST请求,携带XML格式的数据。服务器需要验证签名,确认请求的合法性,然后处理业务逻辑,比如更新订单状态。处理完成后,需要返回成功的XML响应,否则微信会多次通知。 查询订单状态可能也是需要的,特别是在用户支付后,前端可能需要轮询检查支付状态,或者处理异步通知时出现问题,需要主动查询。 可能还需要考虑异常处理,比如网络错误、签名错误、参数错误等。此外,商户订单号的生成需要保证唯一性,通常可以使用时间戳加随机数的方式。 用户可能对具体的代码实现感兴趣,所以给出一个示例代码会很有帮助。代码应该包括生成订单、签名生成、处理回调等部分。同时,需要注意安全,比如API密钥的保管,防止泄露。 另外,需要注意微信支付的API版本,不同版本可能有不同的要求。比如现在V3版本的API可能之前的V2有所不同,参数签名方式可能会有变化,所以需要参考最新的官方文档。 可能用户在实际开发中会遇到的问题包括签名错误、回调通知处理不当导致重复通知、二维码生成失败等。需要提醒用户仔细检查参数是否正确,尤其是金额单位是否正确(分),以及异步通知的URL是否可访问,并且正确处理响应。 总结来说,回答需要涵盖准备工作、生成预支付订单、生成签名、调用接口、生成二维码、处理回调通知、查询订单等步骤,并提供示例代码注意事项。同时,引导用户参考官方文档,确保信息的准确性。以下是</think>使用 Node.js 实现微信支付 Native 支付(PC网站二维码支付)的核心流程代码示例: --- ### **一、准备工作** 1. 申请微信支付商户号(mchid) AppID 2. 设置 API 密钥(`API_KEY`) 3. 准备证书文件(部分接口需要) 4. 确保服务器 IP 加入微信支付白名单 --- ### **二、核心流程** 1. **生成预支付订单** ```javascript const crypto = require('crypto'); const axios = require('axios'); const xml2js = require('xml2js'); // 微信支付配置 const config = { appid: '你的AppID', mch_id: '商户号', key: 'API密钥', notify_url: 'https://yourdomain.com/notify' // 支付结果回调地址 }; // 生成随机字符串 function generateNonceStr() { return Math.random().toString(36).substr(2, 15); } // 生成签名(HMAC-SHA256) function createSign(params) { const sortedParams = Object.keys(params).sort().map(key => `${key}=${params[key]}`).join('&'); const signStr = sortedParams + `&key=${config.key}`; return crypto.createHash('sha256').update(signStr, 'utf8').digest('hex').toUpperCase(); } // 统一下单接口 async function createOrder(orderData) { const params = { appid: config.appid, mch_id: config.mch_id, nonce_str: generateNonceStr(), body: orderData.description, // 商品描述 out_trade_no: orderData.tradeNo, // 商户订单号 total_fee: orderData.amount, // 金额(单位:分) spbill_create_ip: orderData.clientIp, notify_url: config.notify_url, trade_type: 'NATIVE' }; params.sign = createSign(params); // 构造 XML 请求体 const builder = new xml2js.Builder(); const xmlBody = builder.buildObject({ xml: params }); // 发送请求 const response = await axios.post('https://api.mch.weixin.qq.com/pay/unifiedorder', xmlBody, { headers: { 'Content-Type': 'application/xml' } }); // 解析 XML 响应 const result = await xml2js.parseStringPromise(response.data, { explicitArray: false }); return result.xml; } ``` 2. **生成支付二维码** ```javascript const QRCode = require('qrcode'); // 使用返回的 code_url 生成二维码 async function generateQRCode(codeUrl) { try { const qr = await QRCode.toDataURL(codeUrl); return `<img src="${qr}">`; // 返回 base64 图片或保存为文件 } catch (err) { throw new Error('生成二维码失败'); } } ``` 3. **处理支付回调** ```javascript const express = require('express'); const app = express(); app.post('/notify', express.text({ type: 'application/xml' }), async (req, res) => { // 解析 XML 通知 const result = await xml2js.parseStringPromise(req.body, { explicitArray: false }); const notifyData = result.xml; // 验证签名 const sign = notifyData.sign; delete notifyData.sign; const localSign = createSign(notifyData); if (sign === localSign) { if (notifyData.result_code === 'SUCCESS') { // 处理支付成功逻辑(更新订单状态) console.log('支付成功:', notifyData.out_trade_no); res.send('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>'); } } else { res.status(403).send('签名验证失败'); } }); ``` --- ### **三、调用示例** ```javascript // 创建订单 const orderData = { tradeNo: 'ORDER_123456', description: '测试商品', amount: 100, // 1元 = 100分 clientIp: '用户IP' }; createOrder(orderData) .then(response => { if (response.return_code === 'SUCCESS') { generateQRCode(response.code_url).then(qr => { // 将 qr 返回给前端展示 }); } }) .catch(console.error); ``` --- ### **四、注意事项** 1. **金额单位**:微信支付使用 `分` 作为单位(例如 1元 = 100分) 2. **订单号唯一性**:`out_trade_no` 必须全局唯一 3. **超时处理**:二维码有效期为2小时,需处理超时未支付订单 4. **安全验证**:务必验证回调签名,防止伪造请求 5. **证书**:部分敏感接口(如退款)需使用商户证书 --- ### **五、参考文档** - [微信支付 Native 开发文档](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1) - 最新 API 请以微信官方文档为准
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lmr廖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值