【问题标题】:Node.js thumbnailer using Imagemagick: nondeterministic corruption使用 Imagemagick 的 Node.js 缩略图:非确定性损坏
【发布时间】:2012-11-01 12:06:32
【问题描述】:

我有一个 Node.js 服务器,它从数据库 (mongodb) 中的图像 (640x640) 动态生成并提供小 (200x200) 缩略图。我正在使用node-imagemagick 模块进行缩略图。

我的代码在大约 95% 的时间内都能正常工作;客户端 (iOS) 上大约每 20 个(或更少)缩略图图像损坏,报告:

JPEG 损坏的 JPEG 数据:数据段提前结束

对于损坏的图像,客户端显示图像的前 50% - 75%,其余部分被截断。

行为是不确定的,并且损坏的特定图像会根据每个请求而变化。

我正在使用以下代码来调整图像大小并输出缩略图:

im.resize({
  srcData: image.imageData.buffer,
  width: opt_width,
}, function(err, stdout) {
  var responseHeaders = {};
  responseHeaders['content-type'] = 'image/jpeg';
  responseHeaders['content-length'] = stdout.length;
  debug('Writing ', stdout.length, ' bytes.');
  response.writeHead(200, responseHeaders);
  response.write(stdout, 'binary');
  response.end();
});

这里有什么问题?

注意事项:

  1. 问题不在于content-length 标头不正确。当我省略标题时,结果是一样的。
  2. 当我不调整图像大小时,全尺寸图像似乎总是很好。
  3. 在研究这个问题时,我发现了thisthis StackOverflow 问题,它们都通过增加缓冲区大小来解决问题。在我的情况下,图像非常小,所以这似乎不太可能负责。
  4. 我最初将stdout 分配给new Buffer(stdout, 'binary') 并写下它。删除它('binary' 将被弃用)没有任何区别。

【问题讨论】:

  • 几个问题:node.js 环境是什么?数据库中的图像是否损坏,还是仅在 iOS 上损坏?你能创建一个测试方法,你可以从浏览器驱动并在插入数据库之前输出调整大小的图像(到磁盘或浏览器)?
  • 这种情况在一些不同的环境中始终如一地发生,其中包含各种“最新”版本的 Node、ImageMagick 和 mongodb。一个是 Node.js 版本 v0.9.1-pre (OS X),另一个是 0.8.6 (Ubuntu)。我有理由确定源图像没问题,因为如果我不通过 ImageMagick 运行它,它在 iOS 客户端中总是可以正常显示。
  • 情节变厚...当我缓存缩略图(在节点中)以便我可以在调整相同源图像大小时比较输出时,有时存在根本差异(这将与客户端损坏一致)拇指大小。我开始怀疑发送到 IM 的缓冲区不知何故被并发请求阻塞了。
  • 是的,并发请求是我的下一个想法。如果你花时间做一个请求,让它完成,然后再做另一个,它是否工作得很好?是并发问题吗?
  • 不,也不是这样;我现在在将缓冲区提供给 IM 之前复制缓冲区并获得相同的结果。我还将所有拇指写入磁盘;当我对它们运行“识别”时,没有报告任何问题。 (但那是因为识别只读取标题......)@k00k,我会尝试你的建议并将请求限制为一次。

标签: node.js imagemagick


【解决方案1】:

问题似乎是由于node-imagemagick (0.1.2) 的版本稍旧;升级到 0.1.3 是解决方案。

如果这对任何人都有帮助,这里是我用来让 Node.js 排队并一次处理一个客户端请求的代码。

// Set up your server like normal.
http.createServer(handleRequest);
// ...

var requestQueue = [];
var isHandlingRequest = false;  // Prevent new requests from being handled.

// If you have any endpoints that don't always call response.end(), add them here.
var urlsToHandleConcurrently = {
  '/someCometStyleThingy': true
};

function handleRequest(req, res) {
  if (req.url in urlsToHandleConcurrently) {
    handleQueuedRequest(req, res);
    return;
  }
  requestQueue.push([req, res]);  // Enqueue new requests.
  processRequestQueue();          // Check if a request in the queue can be handled.
}

function processRequestQueue() {
  // Continue if no requests are being processed and the queue is not empty.
  if (isHandlingRequest) return;
  if (requestQueue.length == 0) return;

  var op = requestQueue.shift();

  var req = op[0], res = op[1];

  // Wrap .end() on the http.ServerRequest instance to
  // unblock and process the next queued item.
  res.oldEnd = res.end;
  res.end = function(data) {
    res.oldEnd(data);
    isHandlingRequest = false;
    processRequestQueue();
  };

  // Start handling the request, while blocking the queue until res.end() is called.
  isHandlingRequest = true;
  handleQueuedRequest(req, res);
}

function handleQueuedRequest(req, res) {
  // Your regular request handling code here...
}

【讨论】:

    猜你喜欢
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 2012-02-18
    相关资源
    最近更新 更多