用imgproxy自动缩放图片

无图,纯干货,信息量较大,慎入!javascript

最近几天的成果,浓缩下来就是这么一行代码:html

document.getElementById('img1').src = 'http://www.mysite.com/imgproxy' + imgproxy(document.getElementById("img1").getAttribute('data-src'), 135, 85);

寻找合适的图床

最初的时候只是看个人我的博客图片大小高低不一,比较难看,试图找一种方法可以统一各图片的高度。在网上搜索的结果是,发现了几个Jykell的插件,例如jekyll-picture-tag,经过这个过程学到了很多东西,好比img标签除了有srcset之外,还有一个额外的Picture标签等等。原本想用这个插件,但另一个插件jekyll-cloudinary的做者说Picture标签并很差,应该直接使用Cloudinary的服务。java

由此而想起在我上一篇文章中提到过的一篇教程中谈到过的用国内的七牛云作图床,因而开始尝试把我网站文章中用到的图片往七牛云搬家,图片搬家不是问题,但又想在博客网站上增长https服务,因而在问过个人朋友马壮以后,在Cloudflare上开通了https服务,但这又形成另一个问题:七牛云上虽然放了个人图片,可是七牛云自己不支持https服务,因而又得想办法把图片搬到Cloudinary。node

至此我我的的博客算是能够告一段落。平心而论,七牛云的预置功能仍是很不错的,至少它对于URL的处理方式比Cloudinary要简单,但惟一的遗憾是它不支持https。而若是图片不支持https而网站使用https的话,Chrome会在Console里报警告错误,而我对网站的要求是:一个警告都不能有。linux

URL自动调整图片

在此过程当中,我开始思考一个问题:既然Cloudinary七牛云都提供基于URL地址的图片变换,那么它们是怎么作到的呢?根据我对PHP的粗浅了解,最笨的方法能够直接以PHP读文件的方式从硬盘先读取图片的源文件,而后经转换后再以流的方式输出给页面,但这样效率确定极低。因而通过搜索后发现了不少人推荐的libvips库,再进一步搜索,在Github上发现了有不少颗星的imgproxy这个库,彷佛这就是我想要的东西。git

因而我开始尝试动手往公司的服务器上部署imgproxy。但这时候遇到一个问题,在CentOS上,imgproxy并无yum安装包,还须要先手工安装libvips,而后再编译,而最要命的是,公司的服务器在国内,没法经过wget的方式直接安装国外的软件包,由此而我须要先把安装包下载到本地,而后再上传到公司的服务器上。这时候我又想取个巧,使用iterm内置的scp用鼠标拖拽的方式上传文件。按照操做步骤的说明,安装好了以后却发现itermscp按钮依然是灰色的,这时才发现是因为服务器上的fish版本太低,只有1.3,而最新的已是2.6了。因而安装2.6的repo,尝试更新fish,却老是报冲突。由此而想到将fish 1.3先卸载,就在这时灾难发生了。github

灾难

我直接执行了yum remove fish,可是在作这一步以前,我没有将root用户的shell切换回bash,由此而致使了root用户找不到它的shell,由于它还在试图寻找fish。这是一个致命的错误,我记得本身当时隐隐约约有预感,但仍是没有特别在乎,以为也许Linux系统会自动为root用户赋予一个缺省的shell。结果我高估了Linux系统的能力。docker

退出登陆以后,我发现root用户登陆不上了!若是不仔细观察的话,你会感受它的不能登陆的症状和密码错误很是相似,但实际表现其实略有不一样,在SSH端是不大看得出来的。个人第一反应是,若是root用户没法经过SSH登陆了,那么应该经过console端登陆。shell

但当天下午,使人惊讶的是连console端也登不上了!这时候我意识到问题严重了。在网上搜索的结果是有人说应该以runlevel 1的方式登陆,而后尝试修复/etc/shadow。但我彻底不了解对于一台云主机应该如何进入runlevel 1。只好提工单给客服。而客服的技术水平你们应该是知道的,只是建议我重置密码以后再尝试一下。而重置密码必需要关机再重启,就这样来回折腾了好久也修很差。npm

在通过了漫长的等待以后,终于惊动了一个技术人员。他指出若是我必需要进runlevel 1的话,能够在系统开机的前3秒之间按下键盘的e键,而后就能够进入runlevel 1了。

但问题是这是一台云主机,如何能在开机前3秒按键呢?好在如今云主机的console功能很是发达,你能够开着console重启,这时候网络断掉,而后不停地刷新console,你会在电脑开机的一瞬间看到一个有字的黑画面,这时候迅速按下e键也能进入系统。而后再次按下e,把启动模式修改成Linux single

按照他的指导,我终于可以以runlevel 1的方式进入了系统,首先尝试用/etc/passwd重建/etc/shadow,再次重启,无果,仍是登陆不进去。至此为止,全部关于密码的努力均告失败。我想,惟一的办法只能尝试看能不能切换root用户的shell

chsh -s /bin/bash

root用户的shell切换成bash以后,再次重启电脑,果真能够成功登陆了!

修复

接下来,我仍是须要安装fish,但yum install fish结果fish仍是1.3。我还要继续上次不成功的征程。再次把fish1.3换成2.6。依然冲突。此次我学精了,我先把rootshell脚本切换成bash,而后yum remove fish,再次安装,发现这个fish 1.3的来源是一个不知何时装上的名叫dagrepo,因而尝试把这个dagrepo禁止掉:

yum-config-manager --disable dag

而后再次安装,终于装上了fish 2.6

至此,基本全部阻塞性因素都消除了,我开始将libvips的代码拖拽进服务器,而后编译。但这时候问题又来了,imgproxy必须运行在docker里,而说明文档上只说须要本身build一个docker,但并无指明以什么操做系统为基础去build,好在官方提供了一个它们本身的docker文件,能够直接运行imgproxy

啊!早知如此,我何须折腾这么一大圈?还差点毁掉了个人系统。不过好在学到了很多东西。好吧,因而咱们开始直接安装使用imgproxy官方提供的docker

$ docker pull darthsim/imgproxy:latest
$ docker run -e IMGPROXY_KEY=$YOUR_KEY -e IMGPROXY_SALT=$YOUR_SALT -p 8080:8080 -t darthsim/imgproxy

可是这个imgproxy的使用方式又是很是的不友好,它彻底不像七牛云或者Cloudinary那样直接在URL地址上构建就好了,它须要本身根据本身的keysalt产生签名,而后再用签名构建URL,它给了各类语言的例子,惟独没有java的,最后我只好根据它本身的javascript语言的例子构建一个js代码,用于替换页面中的图片连接。

编程

但问题又来了,它给定的这个包是一个nodejs脚本,里面有require语句,没法直接用于浏览器。这时候又得请出browerify,用它来编译node的脚本为能够供浏览器直接使用的脚本。好在过程并不复杂,编译以后获得的bundle.js文件,咱们直接在页面中引用就好了。因而就获得了本文开头的一行代码:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <img id="img1" data-src="http://www.mysite.com/img/somepic.png" src="" />
  <script src="bundle.js"></script>
  <script>
    window.onload = function() {
      document.getElementById('img1').src = 'http://www.mysite.com/imgproxy' + imgproxy(document.getElementById("img1").getAttribute('data-src'), 135, 85);
    }
  </script>
</body>
</html>

以及相关的js:

window.imgproxy = function (url, width, height) {
  const crypto = require('crypto')

  const KEY = 'somekey'
  const SALT = 'somesalt'

  const urlSafeBase64 = (string) => {
    return new Buffer(string).toString('base64').replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
  }

  const hexDecode = (hex) => Buffer.from(hex, 'hex')

  const sign = (salt, target, secret) => {
    const hmac = crypto.createHmac('sha256', hexDecode(secret))
    hmac.update(hexDecode(salt))
    hmac.update(target)
    return urlSafeBase64(hmac.digest())
  }

  const resizing_type = 'fit'
  const gravity = 'no'
  const enlarge = 0
  const extension = 'jpg'
  const encoded_url = urlSafeBase64(url)
  const path = `/${resizing_type}/${width}/${height}/${gravity}/${enlarge}/${encoded_url}.${extension}`

  const signature = sign(SALT, path, KEY)
  const result = `/${signature}${path}`
  return result;
}

固然你须要npm install crypto,而后编译:

browserify main.js > bundle.js

你能够把你本身获得的URL去和这个网站生成的URL作对比,若是彻底一致,就说明你的代码配置正确,不然就仍是有可能不成功。

这就是这两天来的结果。我学到了很多东西,你学到了吗?

相关文章
相关标签/搜索