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

  • Post author:
  • Post category:mysql


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);

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



版权声明:本文为weixin_42493060原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。