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

Android Java中如何通过Bitmap或Base64实现谷歌反向图片搜索

实现向谷歌图片搜索发送图片POST请求的方案

1. 补全Base64编码方法

你现有代码中调用了encodeTobase64但未实现,先补上这个转换Bitmap为Base64字符串的方法:

private String encodeTobase64(Bitmap bitmap) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // 将Bitmap压缩为JPEG格式,质量可根据需求调整(此处设为80)
    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
    byte[] imageBytes = baos.toByteArray();
    return Base64.encodeToString(imageBytes, Base64.DEFAULT);
}

2. 使用谷歌官方Custom Search API(推荐)

谷歌提供了稳定合规的图片反向搜索官方API,需先在谷歌云平台申请API Key和自定义搜索引擎ID(CX)。

基于Volley实现POST请求

private void postRequest(String base64Image) {
    String apiUrl = "https://www.googleapis.com/customsearch/v1";
    
    // 构建请求参数
    HashMap<String, String> params = new HashMap<>();
    params.put("key", "你的API_KEY"); // 替换为你申请的API Key
    params.put("cx", "你的自定义搜索CX"); // 替换为你的自定义搜索引擎ID
    params.put("searchType", "image");
    params.put("imgContent", base64Image); // 传入Base64编码的图片数据
    
    StringRequest stringRequest = new StringRequest(Request.Method.POST, apiUrl,
            response -> {
                // 处理返回的JSON格式搜索结果
                Log.d("GoogleSearch", "搜索结果:" + response);
            },
            error -> {
                // 处理请求错误
                Log.e("GoogleSearch", "请求失败:" + error.getMessage());
            }) {
        @Override
        protected Map<String, String> getParams() {
            return params;
        }
    };
    
    requestQueue.add(stringRequest);
}

注意事项

  • 需在谷歌云平台开通Custom Search API服务,并创建允许搜索全网的自定义搜索引擎以获取CX值。
  • API有免费调用额度,超出后需付费使用。

3. 模拟网页端图片上传(非官方,稳定性差)

若不想使用官方API,可模拟网页端的图片上传逻辑,但谷歌存在反爬机制,可能触发验证码或请求限制。

自定义MultipartRequest处理文件上传

public class MultipartRequest extends Request<String> {
    private final Map<String, String> headers;
    private final Map<String, String> params;
    private final Map<String, DataPart> fileParams;
    private final Listener<String> listener;

    public MultipartRequest(String url, Map<String, String> headers, Map<String, String> params, Map<String, DataPart> fileParams, Listener<String> listener, ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        this.headers = headers;
        this.params = params;
        this.fileParams = fileParams;
        this.listener = listener;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return params;
    }

    @Override
    protected void deliverResponse(String response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String result = null;
        if (response.data != null) {
            try {
                result = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return Response.success(result, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    public String getBodyContentType() {
        return "multipart/form-data; boundary=" + "boundary";
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        try {
            // 添加普通表单参数
            if (params != null && !params.isEmpty()) {
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    buildTextPart(dos, entry.getKey(), entry.getValue());
                }
            }

            // 添加图片文件
            if (fileParams != null && !fileParams.isEmpty()) {
                for (Map.Entry<String, DataPart> entry : fileParams.entrySet()) {
                    buildFilePart(dos, entry.getKey(), entry.getValue());
                }
            }

            dos.writeBytes("--boundary--\r\n");
            dos.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
        dataOutputStream.writeBytes("--boundary\r\n");
        dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"\r\n\r\n");
        dataOutputStream.writeBytes(parameterValue + "\r\n");
    }

    private void buildFilePart(DataOutputStream dataOutputStream, String parameterName, DataPart dataFile) throws IOException {
        dataOutputStream.writeBytes("--boundary\r\n");
        dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"; filename=\"" + dataFile.getFileName() + "\"\r\n");
        dataOutputStream.writeBytes("Content-Type: " + dataFile.getMimeType() + "\r\n\r\n");
        dataOutputStream.write(dataFile.getContent());
        dataOutputStream.writeBytes("\r\n");
    }

    public static class DataPart {
        private String fileName;
        private byte[] content;
        private String mimeType;

        public DataPart(String fileName, byte[] content) {
            this.fileName = fileName;
            this.content = content;
            this.mimeType = "image/jpeg";
        }

        public String getFileName() { return fileName; }
        public byte[] getContent() { return content; }
        public String getMimeType() { return mimeType; }
    }
}

使用MultipartRequest发送图片

修改你的postRequest方法,直接传入Bitmap而非Base64字符串:

private void postRequest(Bitmap bitmap) {
    String searchUrl = "https://images.google.com/searchbyimage/upload";
    
    // 将Bitmap转为字节数组
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
    byte[] imageBytes = baos.toByteArray();
    
    // 构建文件参数
    Map<String, MultipartRequest.DataPart> fileParams = new HashMap<>();
    fileParams.put("encoded_image", new MultipartRequest.DataPart("search_image.jpg", imageBytes));
    
    // 网页端所需的额外表单参数
    Map<String, String> params = new HashMap<>();
    params.put("image_url", "");
    params.put("filename", "");
    params.put("hl", "zh-CN");
    
    // 设置请求头模拟浏览器行为,降低被反爬概率
    Map<String, String> headers = new HashMap<>();
    headers.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36");
    
    MultipartRequest multipartRequest = new MultipartRequest(searchUrl, headers, params, fileParams,
            response -> {
                // 返回结果为HTML页面,需自行解析提取搜索内容
                Log.d("GoogleSearch", "页面内容:" + response);
            },
            error -> {
                Log.e("GoogleSearch", "请求失败:" + error.getMessage());
            });
    
    requestQueue.add(multipartRequest);
}

注意事项

  • 此方式依赖谷歌网页端的接口逻辑,接口规则可能随时变更,稳定性无法保证。
  • 频繁请求可能触发谷歌的反爬机制,导致请求失败或需要验证。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 22:50:42