40 行
1.4 KiB
JavaScript
40 行
1.4 KiB
JavaScript
import { execFileSync } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
import { join, resolve } from 'node:path'
|
|
import { requireAdmin } from '../../utils/auth.js'
|
|
|
|
/**
|
|
* 对 public/uploads 下的图片跑 tesseract chi_sim OCR。
|
|
* Body: { image: '/uploads/reviews/xxx.jpg' }
|
|
* 仅允许 /uploads/ 前缀,防止任意文件读取。
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
requireAdmin(event)
|
|
const { image } = await readBody(event)
|
|
|
|
if (!image || typeof image !== 'string' || !image.startsWith('/uploads/')) {
|
|
throw createError({ statusCode: 422, message: '仅支持 /uploads/ 下的图片' })
|
|
}
|
|
|
|
const rel = image.replace(/^\/+/, '')
|
|
const abs = resolve(join(process.cwd(), 'public', rel))
|
|
const allowedRoot = resolve(join(process.cwd(), 'public/uploads'))
|
|
if (!abs.startsWith(allowedRoot + '/') && abs !== allowedRoot) {
|
|
throw createError({ statusCode: 400, message: '非法路径' })
|
|
}
|
|
if (!existsSync(abs)) {
|
|
throw createError({ statusCode: 404, message: '图片不存在' })
|
|
}
|
|
|
|
try {
|
|
const text = execFileSync('tesseract', [abs, 'stdout', '-l', 'chi_sim'], {
|
|
encoding: 'utf8',
|
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
maxBuffer: 10 * 1024 * 1024,
|
|
})
|
|
return { text: text || '' }
|
|
} catch (e) {
|
|
throw createError({ statusCode: 500, message: 'OCR 执行失败(请确认已安装 tesseract + chi_sim 语言包)' })
|
|
}
|
|
})
|