从 HttpURLConnection 获取 InputStream 对象时出现 FileNotFoundException

Posted

技术标签:

【中文标题】从 HttpURLConnection 获取 InputStream 对象时出现 FileNotFoundException【英文标题】:FileNotFoundException while getting the InputStream object from HttpURLConnection 【发布时间】:2011-07-19 18:02:48 【问题描述】:

我正在尝试使用 HttpURLConnection(用于在 java 中使用 cUrl)向 url 发送 post 请求。 请求的内容是 xml,在终点,应用程序处理 xml 并将记录存储到数据库中,然后以 xml 字符串的形式发回响应。该应用程序在本地托管在 apache-tomcat 上。

当我从终端执行此代码时,会按预期将一行添加到数据库中。但是从连接中获取InputStream的时候抛出异常如下

java.io.FileNotFoundException: http://localhost:8080/myapp/service/generate
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1401)
    at org.kodeplay.helloworld.HttpCurl.main(HttpCurl.java:30)

这里是代码

public class HttpCurl 
    public static void main(String [] args) 

        HttpURLConnection con;

        try 
            con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
            con.setRequestMethod("POST");
            con.setDoOutput(true);
            con.setDoInput(true);

            File xmlFile = new File("test.xml");

            String xml = ReadWriteTextFile.getContents(xmlFile);                

            con.getOutputStream().write(xml.getBytes("UTF-8"));
            InputStream response = con.getInputStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(response));
            for (String line ; (line = reader.readLine()) != null;) 
                System.out.println(line);
            
            reader.close();

         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (MalformedURLException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    
  

它令人困惑,因为异常被跟踪到InputStream response = con.getInputStream(); 行,并且似乎没有涉及 FileNotFoundException 的任何文件。

当我尝试直接打开与 xml 文件的连接时,它不会抛出此异常。

服务应用使用 spring 框架和 Jaxb2Marshaller 创建响应 xml。

ReadWriteTextFile 类取自here

谢谢。

编辑: 嗯,它把数据保存在DB中,同时发回404响应状态码。

我还尝试使用 php 进行 curl 并打印出 CURLINFO_HTTP_CODE,结果是 200。

关于如何调试它的任何想法?服务和客户端都在本地服务器上。

已解决: 在参考 SO 本身的answer 后,我可以解决问题。

似乎 HttpURLConnection 在连接到具有非标准端口的 url 时总是返回 404 响应。

添加这些行解决了它

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");

【问题讨论】:

“当我从终端执行此代码时”- 哪个代码?目前尚不清楚哪些有效与哪些无效。 HttpCurl 是具有此 main 方法的类的名称。这个类是从终端编译运行的 我遇到了同样的问题,但这里的解决方案都不起作用。我最终发现这是 Java 1.7.0_05 的问题,并更新到最新版本 1.7.0_21,问题就消失了。我也意识到这个问题在 Java 1.6 中没有出现。对于仍然卡住的人,仅供参考。 伙计们!请参阅问题的“已解决”评论而不是答案! Read error response body in Java的可能重复 【参考方案1】:

我不知道您的 Spring/JAXB 组合,但一般的 REST Web 服务不会在 POST/PUT 上返回响应正文,只会返回 response status。你想确定它而不是身体。

替换

InputStream response = con.getInputStream();

通过

int status = con.getResponseCode();

所有可用的状态代码及其含义都在 HTTP 规范中可用,如前所述。 Web 服务本身还应该附带一些文档,这些文档概述了 Web 服务支持的所有状态代码及其特殊含义(如果有)。

如果状态以4nn5nn 开头,您希望使用getErrorStream() 来读取可能包含错误详细信息的响应正文。

InputStream error = con.getErrorStream();

【讨论】:

好的,我试过了,它返回状态 404。但奇怪的是,它也在数据库中保存!同样从您提到的内容来看,这是否意味着任何 REST 服务只会返回一个状态码?如果我想返回更多信息,例如 xml 验证错误消息或 url 如果帖子成功,该怎么办? 是的,对于像 POST/PUT/etc 这样的修改请求,它通常不会返回正文。您通常希望在读取输入或错误流之前确定响应状态。我在答案中添加了一些细节。但如果它真的返回状态 404,那么 Web 服务中可能存在一些错误。我会向它的维护者报告。 在这种情况下,服务的维护者恰好是我! ..好吧,我尝试使用php进行curl并返回200(已编辑我的问题)还按照建议尝试了getErrorStream()。它会在 new InputStreamReader(con.getErrorStream()) 上引发 NullPointerException。 对不起,我不熟悉 Spring Web 服务。我只亲身体验过标准 Java EE 5/6 API 中的 JAX-WS/RS。我建议在负责处理 XML 文件的方法上设置一个断点,然后再进一步。 这种行为似乎非常无用,特别是因为它使通过更通用的URLConnection 引用事物成为问题。我想知道为什么 Java 人不按照return responseCode == 200 ? super.getInputStream() : this.getErrorStream() 的方式在HttpUrlConnection 中简单地实现getInputStream()。但无论如何;内容丰富的答案,+1。【参考方案2】:

FileNotFound 只是一个不幸的异常,用于指示 Web 服务器返回 404。

【讨论】:

这并不能解释为什么该行会按照 OP 的说明添加到数据库中。 @BalusC:除非服务器正在添加一行并且然后返回 404。 是的,它似乎以这种方式表现。这是什么意思? @naiquevin:很难说,但鉴于它是本地运行的服务,您应该可以自己调试。 HttpURLConnection 还会为 403 响应(可能还有其他响应)引发 FileNotFoundException。 (即使响应正文 not 似乎是空的。)无论如何,在调用getInputStream() 之前始终调查getResponseCode()【参考方案3】:

对于将来遇到此问题的任何人,原因是状态代码是 404(或者在我的情况下是 500)。当状态码不是 200 时,InpuStream 函数似乎会抛出错误。

在我的情况下,我控制自己的服务器并返回 500 状态代码以指示发生错误。尽管我还发送了带有详细说明错误的字符串消息的正文,但 inputstream 抛出了一个错误,无论正文是否完全可读。

如果你控制你的服务器,我想这可以通过给你自己发送一个 200 状态码然后处理任何字符串错误响应来处理。

【讨论】:

要清楚,你只是处理错了。您应该使用 connection.getResponseCode 检查它是否正常。然后使用 connection.getErrorStream 而不是 getInputStream 来获取错误正文。 (或者您可以使用 getResponseMessage)如果是错误,则不应发送 200 状态代码,请按预期使用 http 错误代码。【参考方案4】:

在这种情况下,FileNotFound 意味着您从服务器收到 404 - 可能是服务器不喜欢“POST”请求?

【讨论】:

但它确实将记录保存到数据库中。 Jaxb2Marshaller 可能是这里的问题吗? 这不仅仅发生在 404 上。任何正文为空的响应也会发生这种情况。 不幸的是,这个答案是错误的。在这种情况下,FileNotFound 仅意味着在响应代码高于 399 时调用了 HttpURLConnection#getInputStream(),而在这种情况下,应该调用了 HttpURLConnection#getErrorStream()。 OTOH,如果服务器不接受 POST,它会返回 405 Method Not Allowed。与被抛出的 FileNotFound 无关。【参考方案5】:

对于遇到此问题的任何其他人,我在尝试将 SOAP 请求标头发送到 SOAP 服务时也遇到了同样的情况。问题是代码中的顺序错误,我在发送 XML 正文之前先请求了输入流。在下面的代码中,InputStream in = conn.getInputStream(); 行紧跟在ByteArrayOutputStream out = new ByteArrayOutputStream(); 之后,这是错误的顺序。

ByteArrayOutputStream out = new ByteArrayOutputStream();
// send SOAP request as part of HTTP body 
byte[] data = request.getHttpBody().getBytes("UTF-8");
conn.getOutputStream().write(data); 

if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) 
  Log.d(TAG, "http response code is " + conn.getResponseCode());
  return null;


InputStream in = conn.getInputStream();

FileNotFound 在这种情况下是一种不幸的方式来编码 HTTP 响应代码 400。

【讨论】:

FileNotFound 是因为连接没有输出流。它与编码响应代码 400 无关。您可以使用 getResponseCode() 获取响应代码。然后,如果不是 HTTP_OK,您应该使用 conn.getErrorStream() 或 getResponseMessage() 获取正文 new ByteArrayOutputStream() 与它无关。问题在于getInputStream()getResponseCode() 之间的顺序。【参考方案6】:

在这种情况下,FileNotFound 意味着您从服务器获得了 404

您必须设置请求 Content-Type 标头参数 将“content-type”请求头设置为“application/json”,以JSON形式发送请求内容。

此参数必须设置为以 JSON 格式发送请求正文。

如果不这样做,服务器会返回 HTTP 状态码“400-bad request”。

con.setRequestProperty("Content-Type", "application/json; utf-8");

完整脚本 ->

public class SendDeviceDetails extends AsyncTask<String, Void, String> 

@Override
protected String doInBackground(String... params) 

    String data = "";
    String url = "";

    HttpURLConnection con = null;
    try 

        // From the above URL object,
        // we can invoke the openConnection method to get the HttpURLConnection object.
        // We can't instantiate HttpURLConnection directly, as it's an abstract class:
        con = (HttpURLConnection)new URL(url).openConnection();
        //To send a POST request, we'll have to set the request method property to POST:
        con.setRequestMethod("POST");

        // Set the Request Content-Type Header Parameter
        // Set “content-type” request header to “application/json” to send the request content in JSON form.
        // This parameter has to be set to send the request body in JSON format.
        //Failing to do so, the server returns HTTP status code “400-bad request”.
        con.setRequestProperty("Content-Type", "application/json; utf-8");
        //Set Response Format Type
        //Set the “Accept” request header to “application/json” to read the response in the desired format:
        con.setRequestProperty("Accept", "application/json");

        //To send request content, let's enable the URLConnection object's doOutput property to true.
        //Otherwise, we'll not be able to write content to the connection output stream:
        con.setDoOutput(true);

        //JSON String need to be constructed for the specific resource.
        //We may construct complex JSON using any third-party JSON libraries such as jackson or org.json
        String jsonInputString = params[0];

        try(OutputStream os = con.getOutputStream())
            byte[] input = jsonInputString.getBytes("utf-8");
            os.write(input, 0, input.length);
        

        int code = con.getResponseCode();
        System.out.println(code);

        //Get the input stream to read the response content.
        // Remember to use try-with-resources to close the response stream automatically.
        try(BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8")))
            StringBuilder response = new StringBuilder();
            String responseLine = null;
            while ((responseLine = br.readLine()) != null) 
                response.append(responseLine.trim());
            
            System.out.println(response.toString());
        

     catch (Exception e) 
        e.printStackTrace();
     finally 
        if (con != null) 
            con.disconnect();
        
    

    return data;


@Override
protected void onPostExecute(String result) 
    super.onPostExecute(result);
    Log.e("TAG", result); // this is expecting a response code to be sent from your server upon receiving the POST data

然后调用它

new SendDeviceDetails().execute("");

您可以在本教程中找到更多详细信息

https://www.baeldung.com/httpurlconnection-post

【讨论】:

【参考方案7】:

解决办法: 只需将 localhost 更改为您 PC 的 IP 如果你想知道这个:Windows+r > cmd > ipconfig 示例:http://192.168.0.107/directory/service/program.php?action=sendSomething 只需将 192.168.0.107 替换为您自己的 IP(不要尝试 127.0.0.1,因为它与 localhost 相同)

【讨论】:

【参考方案8】:

请更改

con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();

【讨论】:

更改它为什么? OP已经明确表示该服务在本地运行。 如果您需要从 localhost 获取图像资源,它不起作用。

以上是关于从 HttpURLConnection 获取 InputStream 对象时出现 FileNotFoundException的主要内容,如果未能解决你的问题,请参考以下文章

如何从 HttpURLConnection 读取 json 数据

Android之HttpURLConnection

android 我用httpurlconnection get方法 从服务器获取了一个json 可是打印出来是一段乱码 代码如下

HttpURLConnection读取403错误的响应内容[重复]

HttpURLConnection 通过代理访问时返回 503 错误

HttpURLConnection.getInputStream() 挂起并且永远不会完成