You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js NSFWJS图像分类API同步问题求助:多图片预测结果异常

Fixing Async Sync & Image Overwriting Issues in Your NSFWJS API

Hey there! Let's tackle the two main issues you're facing—async flow synchronization problems and image overwriting—with some targeted fixes and code refactoring.

Root Causes of Your Problems

  • Async Flow Chaos: Your while loop doesn't wait for asynchronous operations (like downloading images or running model predictions) to finish before moving to the next code. This leads to out-of-order execution, incorrect i increments, and incomplete/overwritten results in your final JSON.
  • Image Overwriting: All images are saved to the same ans1.png file, so each new download replaces the previous one. This means your model ends up predicting on the last downloaded image every time, not the current one.

Refactored Code

const express = require('express')
const multer = require('multer')
const jpeg = require('jpeg-js')
const tf = require('@tensorflow/tfjs-node')
const nsfw = require('nsfwjs')
const bodyParser = require("body-parser")
const axios = require('axios');
const fs = require('fs').promises; // Use promise-based fs for better async handling
const request = require('request');
const { promisify } = require('util');

const app = express()
const upload = multer()
app.use(bodyParser.json());

let _model;

// Promisify request.head for async/await
const requestHead = promisify(request.head);
// Promisify request stream for async download
const downloadImage = async (uri, filename) => {
  await requestHead(uri);
  return new Promise((resolve, reject) => {
    request(uri)
      .pipe(fs.createWriteStream(filename))
      .on('close', resolve)
      .on('error', reject);
  });
};

// Process image and get NSFW predictions
const processImagePrediction = async (imagePath) => {
  const fileContent = await fs.readFile(imagePath);
  const image = jpeg.decode(fileContent, true);
  
  const numChannels = 3;
  const numPixels = image.width * image.height;
  const values = new Int32Array(numPixels * numChannels);
  
  for (let i = 0; i < numPixels; i++) {
    for (let c = 0; c < numChannels; ++c) {
      values[i * numChannels + c] = image.data[i * 4 + c];
    }
  }
  
  const tensor = tf.tensor3d(values, [image.height, image.width, numChannels], 'int32');
  const predictions = await _model.classify(tensor);
  
  // Clean up tensor to free memory
  tensor.dispose();
  return predictions;
};

app.post('/nsfw', async (req, res) => {
  const arrayCodes = req.body.Codes;
  const jsonObj = {};

  if (!arrayCodes || !Array.isArray(arrayCodes)) {
    return res.status(400).json({ error: 'Invalid input: Codes must be an array' });
  }

  for (const code of arrayCodes) {
    try {
      const imgUrl = `http://url/to/image${code}`;
      const response = await axios.get(imgUrl);
      const imageUrl = response.data.facets.Media.productImage.url;
      const tempFilename = `temp-${code}.png`;

      // Download the image to a unique temp file
      await downloadImage(imageUrl, tempFilename);
      // Get predictions
      const predictions = await processImagePrediction(tempFilename);
      // Store results
      jsonObj[code] = predictions;
      // Clean up temp file
      await fs.unlink(tempFilename);
      
      console.log(`Processed code: ${code}`);
    } catch (error) {
      console.error(`Failed to process code ${code}:`, error.message);
      jsonObj[code] = { error: `Failed to process: ${error.message}` };
    }
  }

  res.json(jsonObj);
});

const load_model = async () => {
  _model = await nsfw.load();
  console.log('NSFW model loaded successfully');
};

load_model().then(() => app.listen(8080, () => {
  console.log('Server running on port 8080');
}));

Key Improvements Explained

  1. Sequential Async Execution: Using for...of with await ensures we fully process one image before moving to the next. No more messy loop logic or out-of-sync state.
  2. Unique Temp Files: Each image is saved to a unique file (e.g., temp-Code1.png) so downloads don't overwrite each other. We also delete these files after processing to keep your filesystem clean.
  3. Modular Functions: downloadImage and processImagePrediction separate concerns, making the code easier to read, test, and modify.
  4. Promise-Based Filesystem: Using fs.promises and promisifying request functions lets us use await for all async operations, avoiding callback hell.
  5. Memory Cleanup: We dispose of the TensorFlow tensor after prediction to prevent memory leaks.
  6. Better Error Handling: Each code's processing is wrapped in try/catch, so a single failure doesn't break the entire request. We also return error details for failed codes in the final JSON.
  7. Input Validation: We check if Codes is a valid array to return a clear error for bad requests.

内容的提问来源于stack exchange,提问作者Madhur Dheer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 17:39:08