详解用node-images 打造简易图片服务器

Edit:2016-5-11 修正了代码里面一些明显的错误,并发布在 ajaxjs 库之中,源码在这里。

Edit:2016-5-24 加入 HEAD 请求,检测图片大小。如果小于 80kb 则无须压缩,返回 302 重定向。

node HEAD 请求

var http = require('http');
var url = require('url');
var siteUrl = url.parse('http://img1.gtimg.com/view/pics/hv1/42/80/2065/134297067.jpg'); 

request = http.request({
  method : 'HEAD',
  port: siteUrl.port || 80,
  host: siteUrl.host,
  path : siteUrl.pathname
});
request.on('response', function (response) {
  response.setEncoding('utf8');
  console.log(response.headers['content-length']);
});
request.end(); 

必须先赞下国人 npm 库作品:node-images(https://github.com/zhangyuanwei/node-images),封装了跨平台的 C++ 逻辑,形成 nodejs API 让我们这些小白愉快地使用。之前用过 GraphicsMagick for nodejs,功能最强大,但包体积也比较大,依赖度高,最近好像还爆出了漏洞事件。node-images 相比 GM,主要是更轻量级,无需安装任何图像处理库。

安装 node-images:

npm install images 

npm 包比较大,node_modules 里面有个 node-images.tar.gz 压缩包,下载完之后可以删掉,但剩余也有 11mb。

图片服务器,当前需求是:一个静态服务器,支持返回 jpg/png/gif 即可;支持 HTTP 缓存;支持指定图片分辨率;支持远程图片加载。加载远程图片,可通过设置 maxLength 来限制图片文件大小。

实施过程中,使用 Step.js 参与了异步操作,比较简单。

服务器的相关配置:

// 配置对象。
var staticFileServer_CONFIG = {
  'host': '127.0.0.1',      // 服务器地址
  'port': 3000,        // 端口
  'site_base': 'C:/project/bigfoot',   // 根目录,虚拟目录的根目录
  'file_expiry_time': 480,    // 缓存期限 HTTP cache expiry time, minutes
  'directory_listing': true    // 是否打开 文件 列表
}; 

请求例子:

http://localhost:3001/asset/coming_soon.jpg?w=300
http://localhost:3001/asset/coming_soon.jpg?h=150
http://localhost:3001/asset/coming_soon.jpg?w=300&h=150
http://localhost:3001/?url=http://s0.hao123img.com/res/img/logo/logonew.png

完整源码:

const
  HTTP = require('http'), PATH = require('path'), fs = require('fs'), CRYPTO = require('crypto'),
  url = require("url"), querystring = require("querystring"),
  Step = require('./step'), images = require("images"); 

// 配置对象。
var staticFileServer_CONFIG = {
  'host': '127.0.0.1',      // 服务器地址
  'port': 3001,            // 端口
  'site_base': 'C:/project/bigfoot',     // 根目录,虚拟目录的根目录
  'file_expiry_time': 480,    // 缓存期限 HTTP cache expiry time, minutes
  'directory_listing': true    // 是否打开 文件 列表
}; 

const server = HTTP.createServer((req, res) => {
  init(req, res, staticFileServer_CONFIG);
}); 

server.listen(staticFileServer_CONFIG.port, staticFileServer_CONFIG.host, () => {
  console.log(`Image Server running at http://${staticFileServer_CONFIG.host}:${staticFileServer_CONFIG.port}/`);
}); 

// 当前支持的 文件类型,你可以不断扩充。
var MIME_TYPES = {
  '.txt': 'text/plain',
  '.md': 'text/plain',
  '': 'text/plain',
  '.html': 'text/html',
  '.css': 'text/css',
  '.js': 'application/javascript',
  '.json': 'application/json',
  '.jpg': 'image/jpeg',
  '.png': 'image/png',
  '.gif': 'image/gif'
}; 

MIME_TYPES['.htm'] = MIME_TYPES['.html']; 

var httpEntity = {
  contentType: null,
  data: null,
  getHeaders: function (EXPIRY_TIME) {
    // 返回 HTTP Meta 的 Etag。可以了解 md5 加密方法
    var hash = CRYPTO.createHash('md5');
    //hash.update(this.data);
    //var etag = hash.digest('hex'); 

    return {
      'Content-Type': this.contentType,
      'Content-Length': this.data.length,
      //'Cache-Control': 'max-age=' + EXPIRY_TIME,
      //'ETag': etag
    };
  }
}; 

function init(request, response) {
  var args = url.parse(request.url).query,     //arg => name=a&id=5
    params = querystring.parse(args); 

  if (params.url) {
    getRemoteImg(request, response, params);
  } else {
    load_local_img(request, response, params);
  }
} 

// 加载远程图片
function getRemoteImg(request, response, params) {
  var imgData = ""; // 字符串
  var size = 0;
  var chunks = []; 

  Step(function () {
      HTTP.get(params.url, this);
    },
    function (res) {
      var maxLength = 10; // 10mb
      var imgData = "";
      if (res.headers['content-length'] > maxLength * 1024 * 1024) {
        server500(response, new Error('Image too large.'));
      } else if (!~[200, 304].indexOf(res.statusCode)) {
        server500(response, new Error('Received an invalid status code.'));
      } else if (!res.headers['content-type'].match(/image/)) {
        server500(response, new Error('Not an image.'));
      } else {
        // res.setEncoding("binary"); //一定要设置response的编码为binary否则会下载下来的图片打不开
        res.on("data", function (chunk) {
          // imgData+=chunk;
          size += chunk.length;
          chunks.push(chunk);
        }); 

        res.on("end", this);
      } 

    },
    function () {
      imgData = Buffer.concat(chunks, size); 

      var _httpEntity = Object.create(httpEntity);
      _httpEntity.contentType = MIME_TYPES['.png'];
      _httpEntity.data = imgData;
      // console.log('imgData.length:::' + imgData.length)
      // 缓存过期时限
      var EXPIRY_TIME = (staticFileServer_CONFIG.file_expiry_time * 60).toString();
      response.writeHead(200);
      response.end(_httpEntity.data);
    }
  ); 

} 

// 获取本地图片
function load_local_img(request, response, params) {
  if (PATH.extname(request.url) === '') {
    // connect.directory('C:/project/bigfoot')(request, response, function(){});
    // 如果 url 只是 目录 的,则列出目录
    console.log('如果 url 只是 目录 的,则列出目录');
    server500(response, '如果 url 只是 目录 的,则列出目录@todo');
  } else {
    var pathname = require('url').parse(request.url).pathname;
    // 如果 url 是 目录 + 文件名 的,则返回那个文件
    var path = staticFileServer_CONFIG.site_base + pathname; 

    Step(function () {
      console.log('请求 url :' + request.url + ', path : ' + pathname);
      fs.exists(path, this);
    }, function (path_exists, err) {
      if (err) {
        server500(response, '查找文件失败!');
        return;
      }
      if (path_exists) {
        fs.readFile(path, this);
      } else {
        response.writeHead(404, { 'Content-Type': 'text/plain;charset=utf-8' });
        response.end('找不到 ' + path + '\n');
      }
    }, function (err, data) {
      if (err) {
        server500(response, '读取文件失败!');
        return;
      }
      var extName = '.' + path.split('.').pop();
      var extName = MIME_TYPES[extName] || 'text/plain'; 

      var _httpEntity = Object.create(httpEntity);
      _httpEntity.contentType = extName;
      var buData = new Buffer(data);
      //images(buData).height(100); 

      var newImage; 

      if (params.w && params.h) {
        newImage = images(buData).resize(Number(params.w), Number(params.h)).encode("jpg", { operation: 50 });
      } else if (params.w) {
        newImage = images(buData).resize(Number(params.w)).encode("jpg", { operation: 50 });
      } else if (params.h) {
        newImage = images(buData).resize(null, Number(params.h)).encode("jpg", { operation: 50 });
      } else {
        newImage = buData; // 原图
      } 

      _httpEntity.data = newImage; 

      // 命中缓存,Not Modified返回 304
      if (request.headers.hasOwnProperty('if-none-match') && request.headers['if-none-match'] === _httpEntity.ETag) {
        response.writeHead(304);
        response.end();
      } else {
        // 缓存过期时限
        var EXPIRY_TIME = (staticFileServer_CONFIG.file_expiry_time * 60).toString(); 

        response.writeHead(200, _httpEntity.getHeaders(EXPIRY_TIME));
        response.end(_httpEntity.data);
      }
    });
  }
}
function server500(response, msg) {
  console.log(msg);
  response.writeHead(404, { 'Content-Type': 'text/plain;charset=utf-8' });
  response.end(msg + '\n');
}
加水印也是可以的。例子如下。
var images = require('images');
var path = require('path');
var watermarkImg = images(path.join(__dirname, 'path/to/watermark.ext'));
var sourceImg = images(path.join(__dirname, 'path/to/sourceImg.ext'));
var savePath = path.join(__dirname, 'path/to/saveImg.jpg'); 

// 比如放置在右下角,先获取原图的尺寸和水印图片尺寸
var sWidth = sourceImg.width();
var sHeight = sourceImg.height();
var wmWidth = watermarkImg.width();
var wmWidth = watermarkImg.height(); 

images(sourceImg)
 // 设置绘制的坐标位置,右下角距离 10px
 .draw(watermarkImg, sWidth - wmWidth - 10, sHeight - wmHeight - 10)
 // 保存格式会自动识别
 .save(savePath);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 图片上传之FileAPI与NodeJs

    HTML5之fileAPI HTML5之fileAPI使得我们处理图片上传更加简单. 实例 html代码 <div class="form-group"> <label for="modal_inputFile" class="col-md-3 control-label label-font">位置图:</label> <div class="col-md-9"> <in

  • 简单实现node.js图片上传

    本文实例为大家分享了node.js图片上传的具体代码,供大家参考,具体内容如下 1.node-formidable 对文件上传提供帮助的组件 2.app.js var formidable = require('formidable'); var http = require( 'http' ); var sys = require('sys'); http.createServer(function( request ,response ){ if( request.url == '/uplo

  • node.js实现多图片上传实例

    先上效果图: 这是我当时做多图片的代码,拿出来给大家借鉴一下(有些地方需要亲们自己改一下,大方向是对的) 总共涉及到三处文件(常规来说) 1.路由入口文件(我这里是/routes.js,很多时候会在/app.js) 复制代码 代码如下: //添加美食  app.all('/add', users.add); 2.路由控制器文件(我这里是/routes/users.js) 复制代码 代码如下: //添加美食exports.add = function (req, res) {   if (req.

  • node.js解决获取图片真实文件类型的问题

    遇到一个需求:假定有一个图片文件,真实的类型为jpg,而有人偷懒把jpg直接复制一张,存为同名的png文件,这样在as3读取文件时不会遇到问题,但手机c++在读取文件时却遇到问题了 - -! 现在就需要写一个程序,遍历所有文件夹下的文件,查找文件格式"不正常"的文件.我们的资源主要是gif.png.jpg,最开始,我到网上找到一篇文章:根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,对与通过后缀名获得的文件类型进行比较. 复制代码

  • 轻松创建nodejs服务器(10):处理上传图片

    本节我们将实现,用户上传图片,并将该图片在浏览器中显示出来. 这里我们要用到的外部模块是Felix Geisendörfer开发的node-formidable模块.它对解析上传的文件数据做了很好的抽象. 要安装这个外部模块,需在cmd下执行命令: 复制代码 代码如下: npm install formidable 如果输出类似的信息就代表安装成功了: 复制代码 代码如下: npm info build Success: formidable@1.0.14 安装成功后我们用request将其引入

  • nodejs和php实现图片访问实时处理

    我在访问时光网.网易云音乐等网站时,发现将它们页面中的一些图片URL修改一下就可以得到不同尺寸的图片,于是思考了其实现方案,我的思路是:URL Rewrite + 实时处理 + 缓存,对用户请求的URL进行重写,然后利用图片处理类库对图片进行处理,接着缓存该尺寸图片并输出到浏览器.使用PHP和Node.js实现了一遍,基本达到了需要的效果. 1.Nginx+Node.js(express)实现 URL重写 这里Nginx主要是做一个URL重写和反向代理的功能,配置如下所示: location ~

  • 使用Node.js给图片加水印的方法

    一.准备工作: 首先,确保你本地已经安装好了node环境. 然后,我们进行图像编辑操作需要用到一个Node.js的库:images. 这个库的地址是:https://github.com/zhangyuanwei/node-images,作者定义它为 "Node.js轻量级跨平台图像编解码库" ,并提供了一系列接口. 我们要做的首先是安装images库: npm install images 二.直接上DEMO: 步骤如下: step1:文件夹结构 step2:JS代码 var ima

  • nodejs 整合kindEditor实现图片上传

    kindEditor官网上中提供了ASP,ASP.NET,JSP相关的整合应用,http://kindeditor.net/docs/upload.html可以参照实现nodejs的整合,发现实用nodejs更简单 环境: unbuntu 14.10 nodejs 0.10.35 express 4.11.2 formidable 1.0.16 kindEditor 4.1.10 webStorm 8 1.通过IDE或终端创建一个名称为test的工程 2.编辑package.json添加form

  • 全面解析node 表单的图片上传

    node 全面解析表单的图片上传 ,multiparty解析与内容类型的HTTP请求multipart/form-data,也被称为文件上传. multiparty安装 npm install multiparty html代码 <form action="/api/uppic" method="post" > <input type="file" name="pic" > <input type

  • Node.js批量给图片加水印的方法

    一.准备工作: 首先,你要阅读完这篇文章:http://www.jb51.net/article/97391.htm. 然后,我们安装node.js的一个模块:imageinfo. npm install imageinfo 二.直接上DEMO: 步骤如下: step1:文件夹结构 step2:JS代码 //引用文件系统模块 var fs = require("fs"); //引用imageinfo模块 var imageInfo = require("imageinfo&q

随机推荐