【问题标题】:How do I save a URL to a file after confirming the content type?确认内容类型后如何将 URL 保存到文件中?
【发布时间】:2012-11-09 18:49:04
【问题描述】:

如果文件是特定内容类型,我正在尝试从 URL 下载文件。该 URL 可以提供 html 或 pdf 页面,我只想保存 pdf 文件。我的尝试如下:

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
connection.connect();
String contentType = connection.getContentType();

if (contentType.equals("application/pdf")) {
      org.apache.commons.io.FileUtils.copyURLToFile(url, file);
}

contentType 被正确提取,但对copyURLToFile(url,file); 的调用导致以下异常:

java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(Unknown Source)
at com.sun.net.ssl.internal.ssl.InputRecord.readFully(Unknown Source)
at com.sun.net.ssl.internal.ssl.InputRecord.read(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(Unknown Source)
at com.sun.net.ssl.internal.ssl.AppInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read1(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)
at java.io.FilterInputStream.read(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$HttpInputStream.read(Unknown Source)
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1025)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:999)
at org.apache.commons.io.FileUtils.copyURLToFile(FileUtils.java:848)

如果我删除用于获取 contentType 的代码行,并且只调用copyURLToFile(url,file),则文件已成功下载并保存。我是否以某种方式错误处理了我的HttpURLConnection,导致我的连接被重置?

我还注意到,如果我在if(contentType.equals("application/pdf") 行上设置断点并等待几秒钟,那么对copyURLToFile 的调用会成功,而不会重置连接。我是否引入了某种总是失败的竞争条件?

【问题讨论】:

  • 在获取内容类型或使用 connection.setReadTimeOut 播放后重新连接
  • 我认为HEAD 不应该返回文件的内容。
  • copyURLToFile 的调用会创建一个新连接,因此它不应使用 HEAD 请求从 URL 获取输入流。

标签: java url


【解决方案1】:

你应该使用打开的连接来读取数据:

org.apache.commons.io.IOUtils.copy(connection.getInputStream(), new FileOutputStream(file));

不需要再打开一个连接,可能是服务器重置了连接?

编辑:不是请求方法设置,但是使用 GET 这对我有用:

public static void main(String args[]) throws IOException{
    URL url = new URL("http://www.google.com");
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    String contentType = connection.getContentType();
    System.out.println("content-type: " + contentType);
    IOUtils.copy(connection.getInputStream(), new FileOutputStream("/temp/test.html"));
}

编辑:或者那样,如果你想先检查带有 HEAD 请求的标头:

URL url = new URL("http://www.google.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("HEAD");
String contentType = connection.getContentType();
System.out.println("content-type: " + contentType);
connection.disconnect();
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
IOUtils.copy(connection.getInputStream(), new FileOutputStream("/temp/test.html"));
connection.disconnect();

【讨论】:

  • 我也试过了,但我不确定在发出 HEAD 请求后是否必须以某种方式重置连接。此调用不会导致异常,但 URL 的内容不会保存到文件中。创建一个空文件。我尝试通过connection.setRequestMethod("GET")将请求方法改回GET,但仍然没有运气。
  • 抱歉,您的原始代码中没有看到 HEAD 请求方法。但是使用 GET(请参阅编辑后的答案)它对我有用。所有这些听起来像是服务器完成的连接重置,而不是您的代码有问题。
  • 是的,如果我使用 GET 请求,它就可以工作。我只是试图让 HEAD 请求工作,以便在确认它是我想要保存的内容类型之前没有获取文件的内容。感谢您的回答!我可能会放弃让 HEAD 请求工作而只是浪费带宽...... :)
  • 我做了第三个版本(见答案),对于 google.com,它首先使用 HEAD 请求检查内容类型,然后使用 GET 请求获取内容。
  • 我相信如果你使用 GET 请求,从 header 中读取 content-type,并决定不读取 body,那么你并没有浪费任何带宽。这比关闭连接并打开一个新连接要好,因为建立新连接会增加延迟。
【解决方案2】:

为什么不尝试在阅读 HEAD 后关闭连接?

   HttpURLConnection connection = (HttpURLConnection) url.openConnection();
   connection.setRequestMethod("HEAD");
   connection.connect();
   String contentType = connection.getContentType();
   connection.close();

FileUtils 应该会打开一个新连接,您的问题可能会得到解决。

【讨论】:

  • 我曾尝试在致电getContentType() 后添加connection.disconnect(),但最终还是遇到了同样的异常。
猜你喜欢
  • 2018-05-27
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2013-08-11
  • 1970-01-01
  • 2020-09-28
相关资源
最近更新 更多