前端开发

 首页 > 前端开发 > nodejs > nodejs异步上传图片生成缩略图详解

nodejs异步上传图片生成缩略图详解

分享到:
【字体:
导读:
         [导读] nodejs异步上传图片生成缩略图详解 需求背景 页面一个透明的 file 类型 input 按钮,点击选中图片后自动上传到后台,express 接收后,转给 node 的 upload 模块处理,对图片存储到指定目录,...

nodejs异步上传图片生成缩略图详解

需求背景

页面一个透明的 file 类型 input 按钮,点击选中图片后自动上传到后台,express 接收后,转给 node 的 upload 模块处理,对图片存储到指定目录,并做 80*80 尺寸压缩后,返回图片原图地址及缩略图地址。


方案选用

原则是多快好省,加上可以直接用 jquery 2.0.3,所以就直接挑了支付宝 Arale 中的 arale.upload。


后端的直接从 blueimp 的示例 node 代码里拎出来,自己加工,删掉大部分用不到的,保留了 imagemagic


开始上工

先跑到 github(https://github.com/aralejs/upload) 上温习了一下,就在页面上的对 input 这些元素一一绑定后,就挨个 new Uploader(option) 初始化了。


后端一顿改造后,就算是写完了主逻辑,果断点击上传按钮,走(bugs)起(来了)!


捉虫时间


先是没反应,后台没收到请求,发现是自己弄了个 bx-name="ma/saka/editor/image" 组件没有把模块加载进来,又想了想,把页面上的逻辑丢到 editor 模块里,貌似不太合适,它既要管理数据的校验,还要管理错误信息,并且它依赖 jquery,但 new 的时候,主模块里是没有 jquery 的,必须到了页面上外联了 jquery,才能调用,心一横,直接拎出来到 editor.jade 里丢到 script 标签里先。


再次 file input select, 有了请求,但是报错了:


Error: spawn ENOENT
    at errnoException (child_process.js:988:11)
    at Process.ChildProcess._handle.onexit (child_process.js:779:34)

也看不出个所以然来,就 console.log 开始了噩梦般的断点生涯,也拿不到数据的返回信息,再次心一横:

npm install node-inspector -g

把调试工具配起来,找一个不冲突的端口,跑起来:

node-inspector --web-port=4000

回到主控制台,因为担心会不会是权限问题,就 sudo 一把:

sudo node --debug kirua.js

这次就在 chrome 控制台里开始了开心的断点,总算找到了问题:


var finish = function () {
  counter -= 1
  if (!counter) {
    files.forEach(function (fileInfo) {
      fileInfo.initUrls(handler.req)
    })
    handler.callback({files: files}, redirect)
  }
}

这个 finish 只执行了一次,但 counter 是 2,应该第二次执行后,才会触发 hander.callback ,少了一次,所以一直没返回。


第一次的 finish 是 file data end 的时候自动触发的回调,没问题,所以问题出在了这里:


imageMagick.resize({
  width: opts.width,
  height: opts.height,
  srcPath: options.uploadDir + '/' + fileInfo.name,
  dstPath: options.uploadDir + '/' + version + '/' + fileInfo.name
}, finish)

imageMagick resize 后,没有触发 finish 回调。额,这下可好,只好断点到 node_modules 里的 imagemagick/imagemagick.js 里,一路扒查,找到了:


child.stdout.addListener("data", function (chunk) { std.out(chunk, options.encoding); });
child.stderr.addListener("data", function (chunk) { std.err(chunk, options.encoding); });
var version = process.versions.node.split('.');
child.addListener(version[0] == 0 && version[1] < 7 ? "exit" : "close", function (code, signal) {
  if (timeoutId) clearTimeout(timeoutId);
  if (code === 0 && signal === null) {
    std.finish(null);
  } else {
    var e = new Error("Command "+(timedOut ? "timed out" : "failed")+": " + std.errCurrent());
    e.timedOut = timedOut;
    e.killed = killed;
    e.code = code;
    e.signal = signal;
    std.finish(e);
  }
});

对于 require('child_process').spawn 的这种进程不怎么了解,就 google 了一把大概知道下用意,在群里问了一下,同事说,addListener 这种用法不多见,一般都是 on('data') 的这种,就去挖挖坟:


直接扒到: http://nodejs.org/docs/v0.6.3/api/events.html#emitter.addListener,发现:


server.on('connection', function(stream) {
  console.log('someone connected!')
})
server.addListener('connection', function(stream) {
  console.log('someone connected!')
})

其实这两种效果是等同的, 还有 removeListener removeAllListeners 等等,此时心里着急,再一看到这一行,


version[0] == 0 && version[1] < 7 ? "exit" : "close"

挖到 https://github.com/rsms/node-imagemagick 上一看一年多没维护了,顿时失去了兴致,即便是 blueimp 里用的,果断弃用,换用了更普及的 https://github.com/aheckmann/gm:


想用 gm 还没那么简单,不然怎么就又跑不起来了呢,还得听人家官方的:


First install either GraphicsMagick or ImageMagick. Then:
> npm install gm

莫非是 gm 安装的时候有编译动作,我没看到,我只好再安装一遍,哦,不对,把 GraphicsMagick ImageMagick 都先 brew unlink 下,再重新 install 后,再装 gm,但是没有编译的信息在控制台啊,心理有不好的预感。


果然,还是报错,差不多也是那个不明不白的 spawn ENOENT 错误,我在 chrome 控制台里,拼命 debug,我擦,要死人啊,都 debug 到 streamreadable 和 childprocess 里去了啊,而且在里面点上百下了,出不来了啊。


只好再次求助 google, 在 stackoverflow 上看到有一个家伙说,他自己用:


var gm = require('gm')
var imageMagick = gm.subClass({ imageMagick: true })
这种方法,直接调用 imageMagick 跑起来的 rotate,我为了保持绝对的完全的统一,也贱贱的把我的
gm(srcPath).thumb(opts.width, opts.height, dstPath, 90, function(err){
  if (err) {
    console.dir(err)
  }
  finish()
})
改成了:
imageMagick(srcPath)
  .rotate('yellow', 45)
  .write(dstPath, function (err) {
    if (!err) console.log('crazy pig has arrived')
    else console.log(err)
  })

你妹妹啊,还是不行啊,看来问题不在这儿,继续 google,有人说提到了 mavericks,顿时我脑子一声巨雷,前两天刚刚升级了 mavericks,害我的 node 什么的都用 brew 重新装了一遍,想到这里,汗如雨下,还是老老实实先把环境弄到心理踏实,再来 debug 吧。


嗯,重新做人:


brew update && brew doctor

把 brew 上的有的没得提醒警告全部消停掉,然后 app store 上把 Xcode 更新到最新 - 1.3 个 G 啊,再去 apple developer center 把 commandline tool for mavericks 下载,干等着也是干等着,继续 google, 没找到几个对题的 issues。此时再跑起来服务,因为 brew 弄了一大遍,加上换了 imageMagic = gm.subClass 此时报错是:


{ [Error: Command failed: convert: unable to load module `/usr/local/Cellar/imagemagick/6.8.7-7/lib/ImageMagick//modules-Q16/coders/png.la': file not found @ error/module.c/OpenModule/1277.
convert: no decode delegate for this image format `/Users/huanglong/Projects/central/public/temp/creations/QQ20140113-1 (12).png' @ error/constitute.c/ReadImage/555.
convert: no images defined `/Users/huanglong/Projects/central/public/temp/creations/thumbnail/QQ20140113-1 (12).png' @ error/convert.c/ConvertImageCommand/3145.
就按照这个错误搜到了
https://github.com/Homebrew/homebrew/issues/2415
https://github.com/Homebrew/homebrew/issues/14325

有个家伙说:


brew uninstall imagemagick; brew install imagemagick;brew install libpng; brew link libpng and see if that works?

我试了,发现不行。


还有个家伙说:


Try --build-from-source and let us know. We originally pulled the ImageMagick bottles because our binary distribution system wasn't robust enough to handle things that have dependency trees without the loader links breaking every time a dependency is upgraded.

I've been out of the loop for a couple of months, so some improvements on this front could have occurred that enabled the re-bottling of ImageMagick---but this looks like the same old issue.

好像是他最近几个月不在组织里,所以涉及到的底层的一些改动可能会影响到 imagemagick,但这个问题是个老毛病了之类的,并且其它大多人都围绕着 是否安装了 XQuartz 讨论,我无心看下去,直接就:


brew uninstall imagemagick && brew install imagemagick --build-from-source

然后去吃个了饭,xcode 和 command line tools 装了个遍,重启了个机。


点击 file input, 图片成功的跑到了 thumbnail 里。 Yeah,宣告成功。


原因分析


我升级 mavericks 影响到了 brew 安装过的模块,之前的 brew 安装的 imageMagick 呢不对 mavericks 兼容,现在只要升级 brew 后,解除软连接,并且卸载,再以源安装方式应该就搞定了。


分享到:
更新mac下node环境和npm
今天带来更新mac下node环境和npm 第一步,先查看本机node.js版本: node -v 第二步,清除node.js的cache: sudo npm cache clean -f 第三步,安装 n 工具,这个工具是专门用来管理node.js版本的,别怀疑这个工具的名字,是他是他就是他,他的名字就是 "n" sudo npm install -g n 第四步,安装最新版本的node.js s...
node.js入门实例helloworld详解
本文实例讲述了node.js入门实例helloworld。分享给大家供大家参考,具体如下: 将下面的代码保存为:server.js存到E盘下面的node目录中。 var http = require(&#039;http&#039;); function myNode(request, response){   response.writeHead(200, {&#039;Content-type&#039;:&#039;text/plain&#039;});   res...
  •         php迷,一个php技术的分享社区,专属您自己的技术摘抄本、收藏夹。
  • 在这里……