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

Vapor路由调用ML模型进程返回空字符串问题求助

问题根源
  1. 阻塞事件循环:你在flatMapThrowing里调用的task.waitUntilExit()和readDataToEndOfFile()都是同步阻塞方法,会卡死Vapor的事件循环——进程还没写完输出,你就已经读取了管道数据,自然返回空字符串。
  2. 错误被掩盖:用try!硬启动进程,要是路径不对、权限不够或者ML程序本身报错,直接吞掉错误,要么崩溃要么返回空。
  3. 文件操作虽无大问题,但需确保写入完成:不过这里fileio的write是异步完成后才进入后续逻辑,这个不是主要问题。
修复后的代码
app.post("upload") { req -> EventLoopFuture<String> in
    struct Input: Content {
        var file: File
        var uid: String
        var width: Int
        var height: Int
    }
    let input = try req.content.decode(Input.self)
            
    let path = app.directory.publicDirectory + input.file.filename
            
    print("file:" + input.file.filename + " uid: " + input.uid)
            
    return req.application.fileio.openFile(path: path,
                                           mode: .write,
                                           flags: .allowFileCreation(posixMode: 0x744),
                                           eventLoop: req.eventLoop)
        .flatMap { handle in
            req.application.fileio.write(fileHandle: handle,
                                         buffer: input.file.data,
                                         eventLoop: req.eventLoop)
                .flatMapThrowing { _ in
                    try handle.close()
                    return path
                }
                .flatMap { path in
                    let promise = req.eventLoop.makePromise(of: String.self)
                    let task = Process()
                    task.executableURL = URL(fileURLWithPath: "/usr/local/bin/terminal_machine_learning")
                    task.arguments = [path]

                    let pipe = Pipe()
                    task.standardOutput = pipe
                    task.standardError = pipe // 同时捕获错误输出,方便调试

                    task.terminationHandler = { process in
                        let data = pipe.fileHandleForReading.readDataToEndOfFile()
                        guard let output = String(data: data, encoding: .utf8) else {
                            promise.fail(NSError(domain: "MLProcess", code: -1, userInfo: [NSLocalizedDescriptionKey: "解析进程输出失败"]))
                            return
                        }
                        
                        if process.terminationStatus != 0 {
                            promise.fail(NSError(domain: "MLProcess", code: Int(process.terminationStatus), userInfo: [NSLocalizedDescriptionKey: "进程执行出错: \(output)"]))
                        } else {
                            promise.succeed(output)
                        }
                    }

                    do {
                        try task.run()
                    } catch {
                        promise.fail(error)
                    }

                    return promise.futureResult
                }
        }
}
核心修改说明
  • 异步处理进程:用EventLoopPromise把同步的进程调用转成异步操作,绝不阻塞事件循环,等进程彻底结束再读取输出。
  • 捕获所有输出:把标准错误也导进管道,ML程序跑崩了也能看到报错信息,方便排查。
  • 去掉强制解包:替换try!和!为正经的错误处理,再也不会莫名其妙崩溃或返回空。
  • 用executableURL替代launchPath:苹果推荐的Process用法,比直接写路径更安全。
调试小技巧
  • 先手动跑一遍/usr/local/bin/terminal_machine_learning [你的测试图片路径],确认程序本身能输出内容。
  • 检查图片文件是否真的写到了指定路径,权限是不是够(0x744要保证ML程序能读)。
  • 可以在terminationHandler里加print,把进程状态和输出打出来,看看到底哪一步出问题。

内容的提问来源于stack exchange,提问作者chetan-set

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 04:05:23