一、业务背景
某小程序项目,使用阿里云DCDN全站加速模式进行配置;
图片资源存储到私有OSS桶内,通过OSSFs 工具将OSS挂载到ECS服务器中,当做文件系统进行读取。
二、业务问题
因客户侧缺少设计人员,有些图片运营人员无法自行处理尺寸和体积问题,所以经常会在用小图的位置使用大尺寸的图片。导致本来约定 750像素的移动端海报图,会使用了1920像素的PC端海报图片,因此导致小程序侧图片加载速度缓慢,影响用户体验。
三、提出的解决方案
1、上传时,严格限制图片尺寸和体积大小
此举会对运营侧造成人员成本,且运营团队并非专属团队而是其他运营团队兼顾,因此运营侧和客户拒绝此要求。
2、上传时对图片进行压缩处理
对后台前端技术有一定要求,且压缩处理后的图片无法保证清晰度等问题,如果将处理程序做的很完整带有预览,参数调节等功能的话,开发成本又会增加。
3、借用OSS图片处理规则
相当于第二条的压缩处理,但是因本身就使用的OSS进行图片存储,所以开发成本可以忽略不记。
但是因使用的全站加速,并且挂载OSS到服务器,因此图片处理规则无效,需要重新对图片进行处理。
3.1、将OSS权限设置为公共读,并单独设置CDN域名进行图片资源的访问。
3.2、重写nginx图片反代规则,通过反代实现当前图片链接支持 图片处理规则参数的访问。
最终采用方案
经过沟通讨论后,决定采用 3.2 的解决方案,借用OSS的图片处理功能,同时实现nginx反向代理携带处理参数的功能。
四、详细处理流程
1、安装nginx模块
使用 nginx-njs 模块,进行代码的制作
服务器:Centos 7.9
安装方式:yum
# 安装模块
yum install nginx-module-njs -y
# 修改nginx 配置
vim /etc/nginx/nginx.conf
# 在上方 导入 njs
load_module modules/ngx_http_js_module.so;
# 在 http 标签中配置默认的脚本目录
js_path "/etc/nginx/njs/";
#保存重启nginx
2、编写OSS签名代码
在 /etc/nginx/njs/ 中创建 oss.js 文件
import crypto from 'crypto';
/* oss签名配置数据,换成你自己的PAM账号的AK和bucket */
var ossAccess = {
/* 示例:https://bucketName.oss-cn-chengdu.aliyuncs.com OSS的访问域名*/
region: "",
accessKeyId: "",
accessKeySecret: "",
bucket: "",
};
/**
* 生成加密字符串
* @param {*} method
* @param {*} resourcePath
* @param {*} request
* @param {*} expires
* @returns
*/
function buildCanonicalString(method, resourcePath, request, expires) {
request = request || {};
const headers = request.headers;
const OSS_PREFIX = "x-oss-";
const ossHeaders = [];
const headersToSign = {};
let signContent = [
method.toUpperCase(),
headers["content-md5"] || "",
headers["content-type"],
expires || headers["x-oss-date"],
];
Object.keys(headers).forEach((key) => {
const lowerKey = key.toLowerCase();
if (lowerKey.indexOf(OSS_PREFIX) === 0) {
headersToSign[lowerKey] = String(headers[key]).trim();
}
});
Object.keys(headersToSign)
.sort()
.forEach((key) => {
ossHeaders.push(`${key}:${headersToSign[key]}`);
});
signContent = signContent.concat(ossHeaders);
signContent.push(resourcePath);
return signContent.join("\n");
}
/* 计算签名字符串 */
function computeSignature(accessKeySecret, canonicalString) {
var signature = crypto.createHmac("sha1", accessKeySecret);
return signature
.update(Buffer.from(canonicalString, "utf-8"))
.digest("base64");
}
/* 生成完整认证字符串 */
function authorization(accessKeyId, accessKeySecret, canonicalString) {
return (
"OSS " +
accessKeyId +
":" +
computeSignature(accessKeySecret, canonicalString)
);
}
var GMTdate = "";
/* 返回oss header date字符串 */
function getGMTtime(r) {
/* 函数运行在http环境,如果有多个server最好加上判断条件来避免不必要的消耗 */
/* nginx 环境下 toUTCString 函数和标准有差异, 此处做转换处理,后续njs版本升级后可能需要修改 */
var currentDate = new Date();
var currentDateUTCStr = currentDate.toUTCString();
var dateStrs = currentDateUTCStr.split(" ");
/* 这里需要改动一下处理方式 */
var result =
dateStrs[0] +
" " +
dateStrs[1] +
" " +
dateStrs[2] +
" " +
dateStrs[3] +
" " +
dateStrs[4] +
" GMT";
GMTdate = result;
return result;
}
/* 返回oss header authorization字符串 */
function ossSign(r) {
var method = r.method;
//替换你的文件路径
//参数一:XX.com/upload/XX.jpg
//参数二: "" 代表 OSS的路径,我们存储路径为OSS根目录所以为空
var uri = r.uri.replace("upload/", "");
var headers = new Object();
var process = getProcess(r);
var param = "";
if (process) {
param = "?x-oss-process=" + process;
headers["x-oss-process"] = process;
}
var time = getGMTtime();
var canonicalString = buildCanonicalString(
method,
"/" + ossAccess.bucket + uri + param,
{
headers: headers,
},
time
);
var auth = authorization(
ossAccess.accessKeyId,
ossAccess.accessKeySecret,
canonicalString
);
return auth;
}
function getProcess(r) {
var p = r.args["x-oss-process"];
if (!p) {
p = "";
}
return p;
}
export default { getGMTtime, ossSign, getProcess };
3、修改nginx配置文件
修改站点配置文件
在server 标签中修改反代配置
js_import oss.js;
# 定义签名字符串和GMT时间字符串
js_set $ossDate oss.getGMTtime;
js_set $ossAuth oss.ossSign;
js_set $ossProcess oss.getProcess;
location ^~ /upload/ {
# 设置反向代理时请求header,
proxy_set_header Date $ossDate;
proxy_set_header Authorization $ossAuth;
proxy_set_header x-oss-process $ossProcess;
proxy_pass_header Date;
proxy_pass_header x-oss-process;
proxy_pass_header Authorization;
proxy_pass_request_headers on;
# oss.js 中的 region参数,也是 OSS对外
proxy_pass https://bucketName.oss-cn-chengdu.aliyuncs.com/;
}
重启nginx,测试访问链接