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
相关产品推荐
相关产品推荐

