如何将JSON对象流式传输到HttpURLConnection的POST请求

16

我看不出这段代码有什么问题:

JSONObject msg;  //passed in as a parameter to this method

HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
httpCon.setDoOutput(true);
httpCon.setDoInput(true);
httpCon.setUseCaches(false);
httpCon.setRequestProperty( "Content-Type", "application/json" );
httpCon.setRequestProperty("Accept", "application/json");
httpCon.setRequestMethod("POST");
OutputStream os = httpCon.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
msg.write(osw);
osw.flush();
osw.close();    
os.close();     //probably overkill

在服务器端,我根本没有获取到任何帖子内容,只有一个长度为零的字符串。


我知道看起来很奇怪,但 JSONObject 类有一个 write 方法,你可以将一个 Writer 对象传递给它,然后该类会将自身写入流中。这比将其转换为字符串,然后再将字符串写入流中要高效得多。在这种情况下并没有什么区别,我们可以将任何内容流式传输到 Writer,并且测试的结果都是相同的。错误是关于获取连接的。 - AgilePro
5个回答

26

请尝试

...
httpCon.setRequestMethod("POST");
httpCon.connect(); // Note the connect() here
...
OutputStream os = httpCon.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
...    
osw.write(msg.toString());
osw.flush();
osw.close();

发送数据请尝试:

检索数据请尝试:

BufferedReader br = new BufferedReader(new InputStreamReader( httpCon.getInputStream(),"utf-8"));
String line = null;
while ((line = br.readLine()) != null) {
    sb.append(line + "\n");
}
br.close();
System.out.println(""+sb.toString());

1
我不使用“msg.toString()”的原因是我不想生成第二份数据副本。我经常使用msg.write()来写入文件,所以我知道这样可以正确地写入流中。唯一的区别就是调用“connect”,我会尝试这个。 - AgilePro
是的,“connect”似乎解决了这个问题(这很奇怪,因为服务器收到了请求,所以已经建立了连接,但显然“connect”实际上发送了正文?连接必须在哪里进行约束?在标头之后和getOutputStream之前吗? - AgilePro
JavaDoc说“ URLConnection对象经历两个阶段:首先创建,然后连接。 在创建后,在连接之前可以指定各种选项(例如doInput和UseCaches)。 连接后,尝试设置它们是错误的。 依赖于被连接的操作,如getContentLength,将在必要时隐式执行连接。”。也许close()会强制执行connect(),但flush()太早了?此外,即使在OSW上关闭()也已经触发。 - Yser

5
 public String sendHTTPData(String urlpath, JSONObject json) {
        HttpURLConnection connection = null;
        try {
            URL url=new URL(urlpath);
            connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Accept", "application/json");
            OutputStreamWriter streamWriter = new OutputStreamWriter(connection.getOutputStream());
            streamWriter.write(json.toString());
            streamWriter.flush();
            StringBuilder stringBuilder = new StringBuilder();
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK){
                InputStreamReader streamReader = new InputStreamReader(connection.getInputStream());
                BufferedReader bufferedReader = new BufferedReader(streamReader);
                String response = null;
                while ((response = bufferedReader.readLine()) != null) {
                    stringBuilder.append(response + "\n");
                }
                bufferedReader.close();

                Log.d("test", stringBuilder.toString());
                return stringBuilder.toString();
            } else {
                Log.e("test", connection.getResponseMessage());
                return null;
            }
        } catch (Exception exception){
            Log.e("test", exception.toString());
            return null;
        } finally {
            if (connection != null){
                connection.disconnect();
            }
        }
    }`

在异步任务(AsyncTask)的doInBackground方法中调用此方法。


4

HttpURLConnection 很繁琐。使用 DavidWebb,一个 HttpURLConnection 的微小封装,你可以像这样编写:

JSONObject msg;  //passed in as a parameter to this method

Webb webb = Webb.create();
JSONObject result = webb.post("http://my-url/path/to/res")
    .useCaches(false)
    .body(msg)
    .ensureSuccess()
    .asJsonObject()
    .getBody();

如果你不喜欢它,可以在提供的链接中找到备选库列表。
为什么我们每天都要写相同的样板代码?顺便说一下,上面的代码更易读且更少出错。HttpURLConnection接口很糟糕,必须进行封装!

这个 .body 是否处理流式 JSON,而不是类似 DOM 的 JsonObject 序列化 JSON 的方式? 我的意思是,如果我想使用 GSON 流式处理,如 https://sites.google.com/site/gson/streaming,那么这个 Webb 能处理吗? - diegosasw
看起来非常有趣,因为它支持易用的GZIP压缩,所以我想试一试。但是我找不到任何关于流式JSON支持的信息。 - diegosasw
请查看此问题。感谢@realavaloro。如果我们能找到解决方案,我会更新答案。 - hgoebl
哇,看起来非常漂亮和简单。我如何获取HTTP(s)响应代码?例如200、401..? - CularBytes
1
@RageCompex,你可以访问状态码和其他内容就像这个例子/测试用例中所示。 - hgoebl
谢谢回复,但我在依赖方面遇到了问题,当执行此命令时应用程序崩溃,结果发现我有apache包的问题。尝试使用jarjar重新打包,但目前没有成功:(真的很烦人,这么简单的东西实现起来还是个大麻烦。 - CularBytes

1
跟着这个例子走:
public static PricesResponse getResponse(EventRequestRaw request) {

    // String urlParameters  = "param1=a&param2=b&param3=c";
    String urlParameters = Piping.serialize(request);

    HttpURLConnection conn = RestClient.getPOSTConnection(endPoint, urlParameters);

    PricesResponse response = null;

    try {
        // POST
        OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
        writer.write(urlParameters);
        writer.flush();

        // RESPONSE
        BufferedReader reader = new BufferedReader(new InputStreamReader((conn.getInputStream()), StandardCharsets.UTF_8));
        String json = Buffering.getString(reader);
        response = (PricesResponse) Piping.deserialize(json, PricesResponse.class);

        writer.close();
        reader.close();

    } catch (Exception e) {
        e.printStackTrace();
    }

    conn.disconnect();

    System.out.println("PricesClient: " + response.toString());

    return response;
}


public static HttpURLConnection getPOSTConnection(String endPoint, String urlParameters) {

    return RestClient.getConnection(endPoint, "POST", urlParameters);

}


public static HttpURLConnection getConnection(String endPoint, String method, String urlParameters) {

    System.out.println("ENDPOINT " + endPoint + " METHOD " + method);
    HttpURLConnection conn = null;

    try {
        URL url = new URL(endPoint);
        conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod(method);
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", "text/plain");

    } catch (IOException e) {
        e.printStackTrace();
    }

    return conn;
}

1

这是没有将JSON字符串数据发送到服务器的情况。

 class PostLogin extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... params) {
            String response = null;

            Uri.Builder builder= new Uri.Builder().appendQueryParameter("username","amit").appendQueryParameter("password", "amit");
            String parm=builder.build().getEncodedQuery();
      try
           {

               response = postData("your url here/",parm);
           }catch (Exception e)
           {
               e.printStackTrace();
           }
            Log.d("test", "response string is:" + response);
            return response;
        }
    }


private String postData(String path, String param)throws IOException {
        StringBuffer response = null;

        URL  url = new URL(path);
        HttpURLConnection  connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setDoOutput(true);
//        connection.setRequestProperty("Content-Type", "application/json");
//        connection.setRequestProperty("Accept", "application/json");
            OutputStream out = connection.getOutputStream();
            out.write(param.getBytes());
            out.flush();
            out.close();
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line;
                response = new StringBuffer();
                while ((line = br.readLine()) != null) {
                    response.append(line);
                }
                br.close();
            }

        return response.toString();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接