前言

给AI装上“眼睛”有多简单?此前我们通过终端操作,亲身体验了Ollama运行Qwen 3.5视觉版的强悍本地识图能力——无需联网、不耗API额度,识别精度远超预期。但对于开发者而言,单纯的终端演示远远不够,如何将这种“零成本视觉AI”集成到实际项目中,真正落地到业务场景?

今天就手把手教大家,用Java17+OkHttp打造本地自动识图程序,全程零API费用、数据不出本地,既能精准识别图片内容,还能通过JSON格式与现有业务系统无缝对接,复制代码就能直接用!

1. 先搞懂核心逻辑:程序员眼中的本地Ollama

很多开发者觉得本地AI部署很复杂,其实看透本质后非常简单:Ollama启动后,本质就是一个运行在11434端口的轻量级Web Server。我们无需深入理解神经网络的底层原理,只要用Java发送标准的HTTP POST请求,就能调用Qwen 3.5的视觉推理能力。

核心数据流转流程(极简理解):

  • Java应用:扫描本地指定文件夹,读取图片文件并转为Base64字符串(Ollama接口标准接收格式);

  • HTTP传输:通过OkHttpClient构造JSON请求包,将Base64图片数据发送到本地Ollama服务;

  • 模型推理:Qwen 3.5视觉模型接收请求,完成图片分析推理,返回JSON格式的识别结果;

  • 结果处理:Java解析返回的JSON,清洗无效格式,直接对接业务逻辑(如票据提取、图片内容归档等)。

2. 核心代码实战(复制即用,生产级适配)

实战前先明确环境要求:JDK17+、Ollama已安装(并下载qwen3.5:0.8b模型)、Maven项目。核心依赖选用OkHttp(处理HTTP请求)+Fastjson2(解析JSON),轻量且稳定。

2.1 引入Maven依赖

直接复制到pom.xml中,无需额外调整版本(亲测兼容主流JDK17环境):

<dependencies>
    <!-- OkHttp:优雅处理HTTP请求,支持超时配置 -->
    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>
    <!-- Fastjson2:高效解析JSON,处理响应数据更便捷 -->
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.43</version>
    </dependency>
</dependencies>

2.2 完整Java工具类实现

这不是简单的Demo,而是可直接封装为Service的工具类,包含图片自动扫描、Base64转换、超时防护、响应清洗等生产级特性,注释详细,新手也能看懂每一步作用。

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import okhttp3.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Base64;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * Ollama 本地Qwen 3.5视觉模型调用客户端
 * 功能:自动扫描本地图片、调用模型识别、返回标准JSON结果
 * 可直接集成到SpringBoot等Java项目,封装为Service使用
 * @author 技术干货分享
 */
public class OllamaVisionClient {

    // Ollama本地服务默认API地址(固定端口11434,无需修改)
    private static final String OLLAMA_API_URL = "http://localhost:11434/api/generate";
    // 需与本地Ollama安装的模型名称一致(此处为qwen3.5:0.8b,可根据实际修改)
    private static final String QWEN_MODEL_NAME = "qwen3.5:0.8b";
    // 本地图片存放目录(请替换为自己的图片路径,如D:/imgs)
    private static final String LOCAL_IMAGE_FOLDER = "/Users/wy/Downloads/imgs";
    // OkHttpClient配置:大模型推理耗时较长,放宽超时时间
    private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS) // 连接超时,避免网络波动导致失败
            .readTimeout(120, TimeUnit.SECONDS)   // 读取超时(模型推理耗时)
            .build();
    // 正则表达式:清洗模型返回的Markdown格式,提取纯JSON
    private static final Pattern MARKDOWN_CLEAN_PATTERN =
            Pattern.compile("(?s)^```json\\s*|^```\\s*|```$");

    public static void main(String[] args) {
        // 初始化图片目录
        File imageFolder = new File(LOCAL_IMAGE_FOLDER);
        // 过滤出JPG、PNG格式图片(可根据需求添加JPEG、WEBP等格式)
        File[] imageFiles = imageFolder.listFiles((dir, fileName) ->
                fileName.toLowerCase().endsWith(".jpg") || fileName.toLowerCase().endsWith(".png"));

        // 校验目录是否有图片
        if (imageFiles == null || imageFiles.length == 0) {
            System.out.println("提示:指定目录下无图片文件,请放入JPG/PNG格式图片后重试!");
            return;
        }

        System.out.println("开始执行本地图片识别任务,共发现 " + imageFiles.length + " 张图片...");

        // 批量处理每张图片
        for (File imgFile : imageFiles) {
            try {
                // 核心方法:分析图片并返回结果
                analyzeLocalImage(imgFile);
            } catch (Exception e) {
                System.err.println("处理图片 [" + imgFile.getName() + "] 时出现异常:" + e.getMessage());
            }
        }

        System.out.println("所有图片处理完成!");
    }

    /**
     * 核心方法:读取本地图片,调用Ollama模型进行识别分析
     * @param imgFile 本地图片文件
     * @throws IOException 图片读取或HTTP请求异常
     */
    private static void analyzeLocalImage(File imgFile) throws IOException {
        System.out.println("\n正在读取并分析图片:" + imgFile.getName() + " ...");

        // 步骤1:将图片文件转为Base64字符串(Ollama接口要求的图片格式)
        byte[] imageBytes = Files.readAllBytes(imgFile.toPath());
        String imageBase64 = Base64.getEncoder().encodeToString(imageBytes);

        // 步骤2:构造HTTP请求的JSON参数(Payload)
        JSONObject requestParam = new JSONObject();
        requestParam.put("model", QWEN_MODEL_NAME); // 指定调用的模型
        // 提示词:指定模型返回纯JSON,避免多余格式,方便业务解析
        requestParam.put("prompt", """
                请详细分析这张图片的内容,若是文档、票据类图片,需提取所有关键信息。
                最终结果必须以纯JSON格式返回,严格遵守以下4点要求:
                1. 禁止使用Markdown代码块包裹
                2. 禁止用```json开头或```结尾
                3. 不添加任何多余的解释说明文字
                4. 返回内容必须以{开头、以}结尾,保证JSON格式合法
        """);
        requestParam.put("stream", false); // 关闭流式返回,一次性获取完整结果
        requestParam.put("images", Collections.singletonList(imageBase64)); // 传入Base64图片

        // 步骤3:发送POST请求到Ollama本地服务
        Request request = new Request.Builder()
                .url(OLLAMA_API_URL)
                .post(RequestBody.create(
                        requestParam.toJSONString(),
                        MediaType.parse("application/json")
                ))
                .build();

        // 步骤4:解析响应结果并清洗
        try (Response response = OK_HTTP_CLIENT.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                // 解析返回的JSON数据
                JSONObject responseJson = JSON.parseObject(response.body().string());
                String aiResult = responseJson.getString("response");

                // 清洗结果,去除无效格式,保留纯JSON
                String cleanResult = cleanResponseData(aiResult.trim());

                // 打印识别结果
                System.out.println("[图片分析完成] " + imgFile.getName());
                System.out.println("--------------------------------------------------");
                System.out.println(cleanResult);
                System.out.println("--------------------------------------------------");
            } else {
                System.out.println("请求失败,Ollama服务响应状态码:" + response.code());
            }
        }
    }

    /**
     * 辅助方法:清洗模型返回的响应数据,提取纯JSON内容
     * @param response 模型原始响应字符串
     * @return 清洗后的纯JSON字符串
     */
    public static String cleanResponseData(String response) {
        if (response == null || response.trim().isEmpty()) {
            return "{}"; // 空响应返回空JSON,避免解析异常
        }

        // 1. 去除Markdown代码块包裹(模型可能返回带```的格式)
        String cleanedJson = MARKDOWN_CLEAN_PATTERN.matcher(response).replaceAll("").trim();

        // 2. 容错处理:若JSON不是以{开头,提取第一个{到最后一个}之间的内容
        if (!cleanedJson.startsWith("{")) {
            int jsonStartIndex = cleanedJson.indexOf("{");
            int jsonEndIndex = cleanedJson.lastIndexOf("}");
            if (jsonStartIndex != -1 && jsonEndIndex != -1 && jsonEndIndex > jsonStartIndex) {
                cleanedJson = cleanedJson.substring(jsonStartIndex, jsonEndIndex + 1);
            }
        }

        return cleanedJson;
    }
}

3. 运行效果演示(真实可复现)

运行前准备:

  1. 启动Ollama服务(终端输入ollama serve);

  2. 修改代码中LOCAL_IMAGE_FOLDER为自己的图片目录,放入测试图片(如票据、文档、普通图片);

  3. 运行Java程序,等待识别完成。

实际运行输出(以票据识别为例):

开始执行本地图片识别任务,共发现 1 张图片...

正在读取并分析图片:fapiao.png ...
[图片分析完成] fapiao.png
--------------------------------------------------
{
    "项目名称": "运输服务*客运服务费",
    "单价": "27.72",
    "数量": "1",
    "金额": "27.72",
    "税率/征收率": "3%",
    "税额": "0.83",
    "合计": "¥27.72",
    "价税合计(大写)": "贰拾捌圆伍角伍分",
    "价税合计(小写)": "¥28.55",
    "开票人": "侯娇",
    "出行人": "",
    "有效身份证件号": "",
    "出行日期": "",
    "出发地": "",
    "到达地": "",
    "备注": ""
}
--------------------------------------------------
所有图片处理完成!

说明:识别结果会根据图片内容自动调整,文档类会提取文字,普通图片会详细描述内容,JSON格式可直接用于业务逻辑处理(如存入数据库、展示到前端)。

4. 核心价值总结(开发者必看)

这行代码看似简单,却能帮开发者解决3个核心痛点,真正实现“零成本落地视觉AI”:

4.1 零成本替代云端OCR服务

无需购买百度OCR、阿里OCR等付费API,本地Qwen 3.5视觉模型对常规票据、文档、图片的识别率足以满足大部分业务需求,终身免费,无调用次数限制。

4.2 数据隐私绝对安全

所有图片数据、推理过程都在本地完成,仅在localhost(11434端口)传输,没有任何数据上传到公网。对于金融、医疗、政务等敏感业务场景,这是不可替代的优势。

4.3 无缝对接现有Java项目

可直接将工具类封装为SpringBoot Service,配合Vue3+Element Plus搭建前端上传界面,快速实现“图片上传→本地识别→结果展示”的完整流程,落地到实际业务。

5. 常见问题排查(避坑指南)

  • 请求失败,状态码404:检查Ollama服务是否启动,API地址是否为http://localhost:11434/api/generate;

  • 模型未找到:终端输入ollama pull qwen3.5:0.8b,确保模型下载完成;

  • 超时异常:适当调整OkHttpClient的readTimeout(如改为180秒),大图片推理耗时更长;

  • JSON解析失败:检查提示词是否严格要求“纯JSON返回”,避免模型返回多余文字。

结语:在AI落地的浪潮中,本地部署模型正在成为开发者的首选——零成本、高隐私、可定制。这篇教程的代码可直接复制使用,建议收藏备用,后续会更新SpringBoot集成、前端可视化对接教程,关注不迷路!

如果运行过程中遇到问题,欢迎在评论区留言,一起交流探讨~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐