用 Express 搭建一个 nsfw 服务

工作中遇到的一个问题, 这里简单记录下解决过程.

起因是后台频现用户上传了违规图片, 收到了七牛的警告邮件. 老大要求搭建一个nsfw(Not-Safe-For-Work)服务, 在图片上传到七牛云存储前, 先调用这个服务, 确定图片没有违规, 再上传.

搭建服务

找到了全网star最多的一个开源项目 nsfwjs, 文档中给到了一个完整的 Express 的例子. 照着来就好.

参考 Express Docs 官方文档, 使用express-generator 快速搭个架子, 将 nsfwjs 文档中给到的server.js 部分的示例代码放进去. 本地起服务 node server.js, 使用 curl 测试, 结果符合预期:

curl --request POST localhost:8080/nsfw --header 'Content-Type: multipart/form-data' --data-binary 'image=@/full/path/to/picture.jpg'

支持多格式

nsfwjs 给到的例子中, 只支持JPEG格式的文件, 改用 image-decode 换掉 jpeg-js. 可支持jpeg, jpg, png, gif, bmp, tiff, webp 格式的图片, 基本满足需求.

yarn remove jpeg-js
yarn add image-decode

修改 convert 部分的代码如下:

const decode = require('image-decode')

......

const convert = async (img) => {
  const { data, width, height } = decode(img)

  const numChannels = 3
  const numPixels = width * 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] = data[i * 4 + c]

  return tf.tensor3d(values, [height, width, numChannels], 'int32')
}

CROS 配置

在本地起了 Vue 的一个项目, 在这个 Vue 项目中, 使用 axios 给 nsfw 项目发请求的时候, 会出现 CROS 的报错.

当时直接 Google, StackOverflow 上一堆提到全局设置 header 的, 试了下, 结果无效, 踩了点坑…. 其实直接使用 cors 就能简单粗暴地解决:

yarn add cors

修改 server.js:

var cors = require('cors');
......
var app = express();

......

// cors settings
var corsOptionsDelegate = function (req, callback) {
  const corsOptions = { origin: true, credentials: true }
  callback(null, corsOptions)
}
app.use(cors(corsOptionsDelegate));
......

这里设置了{ origin: true, credentials: true }, cors 的文档中, 默认配置中 origin 为 *, credentials 未设置.

直接使用默认配置会报错:

Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'

干脆直接都设置成true.

再次使用 axios 发送请求, OK.

参考

nsfwjs

Express Docs

image-decode

cors