通过代理下载Java文件

6

我有一个问题,需要通过代理从像www.example.com/example.pdf这样的URL下载文件,并在Java中将其保存到文件系统中。 有人知道这可以如何实现吗?如果我获取InputStream,我可以使用以下方法将其简单地保存到文件系统中:

final ReadableByteChannel rbc = Channels.newChannel(httpUrlConnetion.getInputStream());    
final FileOutputStream fos = new FileOutputStream(file);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();

但是如何通过代理获取URL的输入流呢?如果我像这样做:

SocketAddress addr = new InetSocketAddress("my.proxy.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
URL url = new URL("http://my.real.url.com/");
URLConnection conn = url.openConnection(proxy);

i am getting this exception:

java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(Unknown Source)
    at java.net.SocketInputStream.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 sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown Source)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
    at app.model.mail.crawler.newimpl.FileLoader.getSourceOfSiteViaProxy(FileLoader.java:167)
    at app.model.mail.crawler.newimpl.FileLoader.process(FileLoader.java:220)
    at app.model.mail.crawler.newimpl.FileLoader.run(FileLoader.java:57)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

使用以下代码:

final HttpURLConnection httpUrlConnetion = (HttpURLConnection) website.openConnection(proxy);
httpUrlConnetion.setDoOutput(true);
httpUrlConnetion.setDoInput(true);
httpUrlConnetion.setRequestProperty("Content-type", "text/xml");
httpUrlConnetion.setRequestProperty("Accept", "text/xml, application/xml");
httpUrlConnetion.setRequestMethod("POST");
httpUrlConnetion.connect();

我可以下载网站的 HTML 源代码,但无法下载文件。希望有人能帮我设置正确的属性以便下载文件。


如果您只需要设置代理设置,请参阅 Oracle 的此文档,或者如果想要快速了解,可以查看这个旧的 StackOverflow 问题 - Eric Galluzzo
系统属性无法使用,因为我想在执行下载的每个线程中使用不同的代理。因此,我必须为每个连接设置代理。 - Exagon
上面的Oracle文档指定了如何做到这一点。我添加了一个带有一些示例代码的答案。 - Eric Galluzzo
对我来说不起作用,它给了我一个异常。 - Exagon
也许这不是代理的问题。请参考https://dev59.com/duo6XIcBkEYKwwoYTzNK,了解此错误的原因列表。我猜测是超时了,可以尝试下载一个非常小的文件来检查。 - malarres
4个回答

6

编写程序来设置代理:

SocketAddress addr = new InetSocketAddress("my.proxy.com", 8080);
Proxy proxy = new Proxy(Proxy.Type.HTTP, addr);
URL url = new URL("http://my.real.url.com/");
URLConnection conn = url.openConnection(proxy);

然后,您可以使用上面的代码与最后一行返回的URLConnection一起使用。如果需要,您还可以使用SOCKS代理或强制不使用代理。

这是从Oracle文档中提取的(稍作编辑)。


如果我这样做,会出现异常,请再看一下我的问题,我会进行编辑。 - Exagon
很遗憾,很难确定为什么在您的情况下连接会被重置。您是否尝试使用相同的代理设置在浏览器中访问URL,并确保它在那里工作?您是否使用了正确类型的代理(SOCKS vs. HTTP)? - Eric Galluzzo
我正在使用 SOCKS,是的,我试过了,它可以工作... 我现在尝试了很多其他网站,但从未起作用。 - Exagon
你在代码中将 Proxy.Type.HTTP 改成了 Proxy.Type.SOCKS 吗? 为了保险起见,你可以尝试两种方式。 - Eric Galluzzo
嗯,那我就不确定了。我猜你已经在代码中验证了代理主机和端口。除此之外,我不知道该建议什么了。:( - Eric Galluzzo

5

可以使用Apache httpclient库解决大部分代理问题。要编译以下代码,您可以使用以下maven:

Maven:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>stackoverflow.test</groupId>
  <artifactId>proxyhttp</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>proxy</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.1</version>
    </dependency>
  </dependencies>
</project>

Java代码:

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * How to send a request via proxy.
 *
 * @since 4.0
 */
public class ClientExecuteProxy {

    public static void main(String[] args)throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            HttpHost target = new HttpHost("www.google.com", 80, "http");
            HttpHost proxy = new HttpHost("127.0.0.1", 8889, "http");

            RequestConfig config = RequestConfig.custom()
                    .setProxy(proxy)
                    .build();
            HttpGet request = new HttpGet("/");
            request.setConfig(config);

            System.out.println("Executing request " + request.getRequestLine() + " to " + target + " via " + proxy);

            CloseableHttpResponse response = httpclient.execute(target, request);
            try {
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                System.out.println(EntityUtils.toString(response.getEntity()));
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

}

@Exagon 我更新了代码,因为上一次使用的是我为旧版本编写的类,这些类已经全部过时了。我使用 Fiddler2 作为代理重新测试了代码,它能够正常工作。如果你遇到超时问题,那可能是一个“网络”问题。 - Marco Altieri
@exagon 你使用的是哪个库的版本?如果不想使用maven,你可以从此链接下载我所用的版本:http://central.maven.org/maven2/org/apache/httpcomponents/httpclient/4.5.1/httpclient-4.5.1.jar - Marco Altieri
最新的4.5.1版本,我的IDE是Eclipse Mars,我正在使用Java 8.65。 - Exagon
这是一个编译时错误...我不知道为什么...当我创建一个新项目并添加库和这个类时也会出现。 - Exagon
HttpGet自始就有setConfig方法。正如我所说,这个例子来自于apache httpclient网站,因此它必须能够工作。 - Marco Altieri
显示剩余5条评论

5
以下内容与其他答案不同,适用于我:在连接之前设置这些属性:
            System.getProperties().put("http.proxySet", "true");
            System.getProperties().put("http.proxyHost", "my.proxy.com");
            System.getProperties().put("http.proxyPort", "8080"); //port is String, not int

然后,打开URLConnection并尝试下载文件。

我在不同的线程中使用不同的代理,所以这样做行不通。 - Exagon

2
另一种方法是在每个httpUrlConnection实例中实现代理。具体步骤如下:
  1. 不要直接连接到想要的真实URL,而是先使用http GET方法连接到代理IP和端口,并引用您想要的URL。
  2. 使用setRequestProperty设置host为您的URL以及任何其他必要的标头。

如果成功,该连接将透明地向您发送文件。

我有一些使用Sockets的代码可供参考。

try {
    Socket sock = new Socket("10.0.241.1", 3128); //proxy IP and port
    InputStream is = sock.getInputStream();
    OutputStream os = sock.getOutputStream();
    String str = "GET http://www.uol.com.br HTTP/1.1\r\n"; //GET your site
    str += "Host: www.uol.com.br\r\n"; //again, Host of your site
    str += "Proxy-Authorization: Basic ZWR1YXJkby5wb2NvOmM1NmQyMw==\r\n"; //if password is needed
    str += "\r\n";
    os.write(str.getBytes());
    byte[] bb = new byte[1024];
    int L = 0;
    while ((L = is.read(bb)) != -1) {
        //write bytes to file stream...
    }
} catch (Exception ex) {
    //exception handling...
}

“当有httpUrlConnection这个工具可用时,为什么还要使用纯套接字呢?”你问道。那时候,我还不知道httpUrlConnection的存在。”

你能展示一下如何使用代码来设置所有属性吗? - Exagon
这个实现是在我还不知道httpUrlConnection的时候使用套接字完成的。我匆忙编辑了一下,我认为你可以想出在httpUrlConnection上等效的操作。如果需要,我可以再次编辑以适应httpUrlConnection。 - Eduardo Poço

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