Java执行curl命令获取响应字符串为null的问题排查与解决
问题根源
你的代码拿不到结果是两个核心错误导致的:
Runtime.getRuntime().exec(String)不会解析shell语法:你命令里写的行尾转义\、换行、制表符、包裹URL的双引号都是bash等shell终端才会处理的语法,直接传给exec方法时,这些特殊字符会被当成普通字符拼到参数里,curl收到的参数完全不对,根本发不出正确请求。- 你既没有读取进程的错误输出流,也没有等待进程执行结束:curl执行出错的提示全部输出在标准错误流,你只读取标准输出流自然拿不到任何内容,甚至进程还没执行完你就开始读流,很容易拿到空值。
你之前用HttpClient报认证失败,基本都是两个细节没做对:一是没设置Content-Type: application/x-www-form-urlencoded请求头,二是参数没有做符合规范的URL编码,和curl的--data-urlencode行为不一致,服务端识别不到正确的认证参数就会返回认证失败。
修复方案
方案1:修正curl调用逻辑(不推荐,依赖系统环境)
不要直接拼接带shell转义规则的命令字符串,用ProcessBuilder以参数数组的形式传入命令,不需要额外加引号、换行转义、制表符,同时合并错误流,等待进程执行完成后再读取结果:
import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.stream.Collectors; public class CurlCallTest { public static void main(String[] args) throws Exception { ProcessBuilder pb = new ProcessBuilder( "curl", "-X", "POST", "https://kakaoapi.aligo.in/akv10/token/create/30/s/", "--data-urlencode", "apikey=xxxxx", "--data-urlencode", "userid=xxxxx", "-s" // 静默模式,屏蔽curl自带的进度输出避免干扰结果 ); // 将错误流合并到标准输出流,不需要单独读取两个流 pb.redirectErrorStream(true); Process process = pb.start(); String result = new BufferedReader( new InputStreamReader(process.getInputStream())) .lines() .collect(Collectors.joining("\n")); // 等待进程执行完成,通过退出码判断是否执行成功 int exitCode = process.waitFor(); if (exitCode != 0) { System.out.printf("curl执行失败,退出码:%d,错误信息:%s%n", exitCode, result); return; } System.out.println("接口返回结果:" + result); } }
该方案依赖运行环境预装curl命令,Windows默认环境、精简版容器环境可能不存在curl,生产环境不建议使用。
方案2:原生Java实现HTTP调用(推荐,无外部依赖)
直接用Java原生API发送请求,不需要依赖系统命令,只要参数编码、请求头设置正确,就能和curl拿到完全一致的结果。以Java 11+自带的HttpClient为例:
import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; public class HttpCallTest { public static void main(String[] args) throws Exception { // 组装表单参数 Map<String, String> formParams = new HashMap<>(); formParams.put("apikey", "xxxxx"); formParams.put("userid", "xxxxx"); // 手动做URL编码,和curl --data-urlencode的行为完全一致 String requestBody = formParams.entrySet().stream() .map(entry -> String.format("%s=%s", URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8), URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8) )) .collect(Collectors.joining("&")); HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://kakaoapi.aligo.in/akv10/token/create/30/s/")) // 必须设置表单类型的Content-Type,否则服务端无法识别参数 .header("Content-Type", "application/x-www-form-urlencoded") .POST(HttpRequest.BodyPublishers.ofString(requestBody)) .build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("响应状态码:" + response.statusCode()); System.out.println("响应内容:" + response.body()); } }
如果是Java 8环境,用自带的HttpURLConnection按照相同逻辑实现即可,核心就是保证参数编码规则、请求头和curl调用一致,就不会出现认证失败的问题。
内容的提问来源于stack exchange,提问作者Adam Lee
相关产品推荐
相关产品推荐

