【问题标题】:How can I download and save a file from the Internet using Java?如何使用 Java 从 Internet 下载和保存文件?
【发布时间】:2010-10-29 14:34:04
【问题描述】:

有一个在线文件(如http://www.example.com/information.asp)我需要抓取并保存到一个目录。我知道有几种方法可以逐行抓取和读取在线文件 (URL),但有没有办法只使用 Java 下载和保存文件?

【问题讨论】:

标签: java download


【解决方案1】:

underscore-java 库中有一个方法 U.fetch(url)

文件pom.xml

<dependency>
  <groupId>com.github.javadev</groupId>
  <artifactId>underscore</artifactId>
  <version>1.74</version>
</dependency>

代码示例:

import com.github.underscore.U;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Download {
    public static void main(String[] args) throws IOException {
        Files.write(Paths.get("data.bin"),
            U.fetch("https://stackoverflow.com/questions"
                + "/921262/how-to-download-and-save-a-file-from-internet-using-java").blob());
    }
}

【讨论】:

  • 当链接失效时,这个答案有多大用处?请看How to Answer
  • 您的代码无法编译。问题在Java 寻求解决方案,但您的答案看起来像JavaScript
  • @talex 我添加了 pom.xml 部分并改进了代码示例。
  • 问题是关于将数据保存到目录中的文件,而不是全部读入字符串,这在二进制数据的情况下无效。
  • @user207421 我添加了保存二进制文件的示例。 Files.write(Paths.get("data.bin"), U.fetch(url).blob()).
【解决方案2】:

如果您在代理后面,您可以在 Java 程序中设置代理,如下所示:

Properties systemSettings = System.getProperties();
systemSettings.put("proxySet", "true");
systemSettings.put("https.proxyHost", "HTTPS proxy of your org");
systemSettings.put("https.proxyPort", "8080");

如果您没有使用代理,请不要在代码中包含上述行。使用代理时下载文件的完整工作代码。

public static void main(String[] args) throws IOException {
    String url = "https://raw.githubusercontent.com/bpjoshi/fxservice/master/src/test/java/com/bpjoshi/fxservice/api/TradeControllerTest.java";
    OutputStream outStream = null;
    URLConnection connection = null;
    InputStream is = null;
    File targetFile = null;
    URL server = null;

    // Setting up proxies
    Properties systemSettings = System.getProperties();
        systemSettings.put("proxySet", "true");
        systemSettings.put("https.proxyHost", "HTTPS proxy of my organisation");
        systemSettings.put("https.proxyPort", "8080");
        // The same way we could also set proxy for HTTP
        System.setProperty("java.net.useSystemProxies", "true");
        // Code to fetch file
    try {
        server = new URL(url);
        connection = server.openConnection();
        is = connection.getInputStream();
        byte[] buffer = new byte[is.available()];
        is.read(buffer);

        targetFile = new File("src/main/resources/targetFile.java");
        outStream = new FileOutputStream(targetFile);
        outStream.write(buffer);
    } catch (MalformedURLException e) {
        System.out.println("THE URL IS NOT CORRECT ");
        e.printStackTrace();
    } catch (IOException e) {
        System.out.println("I/O exception");
        e.printStackTrace();
    }
    finally{
        if(outStream != null)
            outStream.close();
    }
}

【讨论】:

  • systemSettings.put("proxySet", "true"); 是源自于 1998 年失效的 HotJava bean 的都市神话。在任何 Sun 或 Oracle JDK 中,它完全没有任何作用。证明:在任何需要其他代理设置的情况下将其设置为false,并观察它继续工作。
  • 嗯,很有趣,会检查更多。谢谢
【解决方案3】:

总结(并以某种方式润色和更新)以前的答案。以下三种方法实际上是等效的。 (我添加了明确的超时,因为我认为它们是必须的。没有人希望下载在连接断开时永远冻结。)

public static void saveUrl1(final Path file, final URL url,
    int secsConnectTimeout, int secsReadTimeout))
    throws MalformedURLException, IOException {

    // Files.createDirectories(file.getParent()); // Optional, make sure parent directory exists
    try (BufferedInputStream in = new BufferedInputStream(
         streamFromUrl(url, secsConnectTimeout,secsReadTimeout));
         OutputStream fout = Files.newOutputStream(file)) {

            final byte data[] = new byte[8192];
            int count;
            while((count = in.read(data)) > 0)
                fout.write(data, 0, count);
        }
}

public static void saveUrl2(final Path file, final URL url,
    int secsConnectTimeout, int secsReadTimeout))
    throws MalformedURLException, IOException {

    // Files.createDirectories(file.getParent()); // Optional, make sure parent directory exists
    try (ReadableByteChannel rbc = Channels.newChannel(
             streamFromUrl(url, secsConnectTimeout, secsReadTimeout)
        );
        FileChannel channel = FileChannel.open(file,
             StandardOpenOption.CREATE,
             StandardOpenOption.TRUNCATE_EXISTING,
             StandardOpenOption.WRITE)
        ) {

        channel.transferFrom(rbc, 0, Long.MAX_VALUE);
    }
}

public static void saveUrl3(final Path file, final URL url,
    int secsConnectTimeout, int secsReadTimeout))
    throws MalformedURLException, IOException {

    // Files.createDirectories(file.getParent()); // Optional, make sure parent directory exists
    try (InputStream in = streamFromUrl(url, secsConnectTimeout,secsReadTimeout) ) {
        Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
    }
}

public static InputStream streamFromUrl(URL url,int secsConnectTimeout,int secsReadTimeout) throws IOException {
    URLConnection conn = url.openConnection();
    if(secsConnectTimeout>0)
        conn.setConnectTimeout(secsConnectTimeout*1000);
    if(secsReadTimeout>0)
        conn.setReadTimeout(secsReadTimeout*1000);
    return conn.getInputStream();
}

我没有发现显着的差异,而且在我看来一切都是正确的。它们安全高效。 (速度差异似乎无关紧要 - 我将 180 MB 从本地服务器写入 SSD 磁盘的时间大约在 1.2 到 1.5 秒之间波动)。它们不需要外部库。所有这些都适用于任意大小和(根据我的经验)HTTP 重定向。

此外,如果找不到资源(通常为错误 404),则全部抛出 FileNotFoundException,如果 DNS 解析失败,则抛出 java.net.UnknownHostException;其他IOException对应传输过程中的错误。

【讨论】:

    【解决方案4】:

    使用 Java 7+ 时,使用以下方法从 Internet 下载文件并保存到某个目录:

    private static Path download(String sourceURL, String targetDirectory) throws IOException
    {
        URL url = new URL(sourceURL);
        String fileName = sourceURL.substring(sourceURL.lastIndexOf('/') + 1, sourceURL.length());
        Path targetPath = new File(targetDirectory + File.separator + fileName).toPath();
        Files.copy(url.openStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
    
        return targetPath;
    }
    

    文档是here

    【讨论】:

      【解决方案5】:

      此答案几乎与所选答案完全相同,但有两个增强:它是一个方法,它关闭了 FileOutputStream 对象:

          public static void downloadFileFromURL(String urlString, File destination) {
              try {
                  URL website = new URL(urlString);
                  ReadableByteChannel rbc;
                  rbc = Channels.newChannel(website.openStream());
                  FileOutputStream fos = new FileOutputStream(destination);
                  fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
                  fos.close();
                  rbc.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      

      【讨论】:

      • 一次调用是不够的。 transferFrom() isnt' 指定在单个调用中完成整个传输。这就是它返回计数的原因。你必须循环。
      • 如果出现异常,您的代码不会关闭任何内容。
      【解决方案6】:

      简单的用法存在问题:

      org.apache.commons.io.FileUtils.copyURLToFile(URL, File)
      

      如果您需要下载和保存非常大的文件,或者一般情况下如果您需要在连接中断时自动重试。

      在这种情况下,我建议使用 Apache HttpClient,以及 org.apache.commons.io.FileUtils。例如:

      GetMethod method = new GetMethod(resource_url);
      try {
          int statusCode = client.executeMethod(method);
          if (statusCode != HttpStatus.SC_OK) {
              logger.error("Get method failed: " + method.getStatusLine());
          }
          org.apache.commons.io.FileUtils.copyInputStreamToFile(
              method.getResponseBodyAsStream(), new File(resource_file));
          } catch (HttpException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
          method.releaseConnection();
      }
      

      【讨论】:

      • 这段代码在处理重试方面并没有比这里的任何其他答案或评论更好:也就是说,它根本不这样做。
      【解决方案7】:

      使用 Apache Commons IO。就是一行代码:

      FileUtils.copyURLToFile(URL, File)
      

      【讨论】:

      • 不错!正是我要找的!我知道 Apache 库已经涵盖了这一点。 BTW,建议使用带超时参数的重载版本!
      • ...当使用重载版本时,请记住超时以毫秒为单位,而不是秒。
      • 请注意,带有 timeout 参数的copyURLToFile 仅在 Commons IO 库 2.0 版本后可用。见Java docs
      • 如果必须将基本身份验证标头添加到请求中怎么办?有解决办法吗?
      • 这个虽然“短”,但其实很慢。
      【解决方案8】:

      下载文件需要您阅读它。无论哪种方式,您都必须以某种方式浏览文件。您可以从流中按字节读取它,而不是逐行读取:

      BufferedInputStream in = new BufferedInputStream(new URL("http://www.website.com/information.asp").openStream())
      byte data[] = new byte[1024];
      int count;
      while((count = in.read(data, 0, 1024)) != -1)
      {
          out.write(data, 0, count);
      }
      

      【讨论】:

        【解决方案9】:

        这是一个简洁、易读、仅限 JDK 的解决方案,具有正确关闭的资源:

        static long download(String url, String fileName) throws IOException {
            try (InputStream in = URI.create(url).toURL().openStream()) {
                return Files.copy(in, Paths.get(fileName));
            }
        }
        

        两行代码,没有依赖关系。

        这是一个带有输出、错误检查和命令行参数检查的complete file downloader example 程序:

        package so.downloader;
        
        import java.io.IOException;
        import java.io.InputStream;
        import java.net.URI;
        import java.nio.file.Files;
        import java.nio.file.Paths;
        
        public class Application {
            public static void main(String[] args) throws IOException {
                if (2 != args.length) {
                    System.out.println("USAGE: java -jar so-downloader.jar <source-URL> <target-filename>");
                    System.exit(1);
                }
        
                String sourceUrl = args[0];
                String targetFilename = args[1];
        
                long bytesDownloaded = download(sourceUrl, targetFilename);
        
                System.out.println(String.format("Downloaded %d bytes from %s to %s.", bytesDownloaded, sourceUrl, targetFilename));
            }
        
            static long download(String url, String fileName) throws IOException {
                try (InputStream in = URI.create(url).toURL().openStream()) {
                    return Files.copy(in, Paths.get(fileName));
                }
            }    
        }
        

        so-downloader repository README 中所述:

        运行文件下载程序:

        java -jar so-downloader.jar <source-URL> <target-filename>
        

        例如:

        java -jar so-downloader.jar https://github.com/JanStureNielsen/so-downloader/archive/main.zip so-downloader-source.zip
        

        【讨论】:

          【解决方案10】:

          java.net.http.HttpClient 上使用授权的解决方案:

          HttpClient client = HttpClient.newHttpClient();
          
          HttpRequest request = HttpRequest.newBuilder()
                  .GET()
                  .header("Accept", "application/json")
                  // .header("Authorization", "Basic ci5raG9kemhhZXY6NDdiYdfjlmNUM=") if you need
                  .uri(URI.create("https://jira.google.ru/secure/attachment/234096/screenshot-1.png"))
                  .build();
          
          HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
          
          try (InputStream in = response.body()) {
              Files.copy(in, Paths.get(target + "screenshot-1.png"), StandardCopyOption.REPLACE_EXISTING);
          }
          

          【讨论】:

          • “授权”被注释掉。它的作用是什么(根据第一句话的“...使用授权”)?
          • 这甚至不能编译(也由语法 highligting 表示) - 在 "screenshot-1.png"" 附近。
          • @PeterMortensen 如果资源是免费的,则不需要授权。语法错误已修复。
          【解决方案11】:

          这可以读取互联网上的文件并将其写入文件。

          import java.net.URL;
          import java.io.FileOutputStream;
          import java.io.File;
          
          public class Download {
              public static void main(String[] args) throws Exception {
                   URL url = new URL("https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png");  // Input URL
                   FileOutputStream out = new FileOutputStream(new File("out.png"));  // Output file
                   out.write(url.openStream().readAllBytes());
                   out.close();
              }
          }
          

          【讨论】:

            【解决方案12】:

            使用新频道的第一种方法

            ReadableByteChannel aq = Channels.newChannel(new url("https//asd/abc.txt").openStream());
            FileOutputStream fileOS = new FileOutputStream("C:Users/local/abc.txt")
            FileChannel writech = fileOS.getChannel();
            

            使用 FileUtils 的第二种方法

            FileUtils.copyURLToFile(new url("https//asd/abc.txt", new local file on system("C":/Users/system/abc.txt"));
            

            第三种使用方法

            InputStream xy = new ("https//asd/abc.txt").openStream();
            

            这就是我们可以使用基本 Java 代码和其他第三方库下载文件的方式。这些仅供快速参考。请使用以上关键字google 以获取详细信息和其他选项。

            【讨论】:

            • 第三种方法无法编译。第一个和第三个都不会真正复制任何东西。
            【解决方案13】:

            以下是使用 Java 代码从 Internet 下载电影的示例代码:

            URL url = new
            URL("http://103.66.178.220/ftp/HDD2/Hindi%20Movies/2018/Hichki%202018.mkv");
                BufferedInputStream bufferedInputStream = new  BufferedInputStream(url.openStream());
                FileOutputStream stream = new FileOutputStream("/home/sachin/Desktop/test.mkv");
            
                int count = 0;
                byte[] b1 = new byte[100];
            
                while((count = bufferedInputStream.read(b1)) != -1) {
                    System.out.println("b1:" + b1 + ">>" + count + ">> KB downloaded:" + new File("/home/sachin/Desktop/test.mkv").length()/1024);
                    stream.write(b1, 0, count);
                }
            

            【讨论】:

            • 一般来说,如果答案包含对代码的用途的解释,以及为什么在不介绍其他人的情况下解决问题的原因,答案会更有帮助。
            • 这段代码从不关闭任何东西,并且使用了一个可笑的小缓冲区,
            【解决方案14】:

            您可以在一行中使用netloader for Java

            new NetFile(new File("my/zips/1.zip"), "https://example.com/example.zip", -1).load(); // Returns true if succeed, otherwise false.
            

            【讨论】:

              【解决方案15】:

              可以使用 Apache 的 HttpComponents 而不是 Commons IO 下载文件。此代码允许您根据其 URL 下载 Java 中的文件并将其保存在特定的目标位置。

              public static boolean saveFile(URL fileURL, String fileSavePath) {
              
                  boolean isSucceed = true;
              
                  CloseableHttpClient httpClient = HttpClients.createDefault();
              
                  HttpGet httpGet = new HttpGet(fileURL.toString());
                  httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0");
                  httpGet.addHeader("Referer", "https://www.google.com");
              
                  try {
                      CloseableHttpResponse httpResponse = httpClient.execute(httpGet);
                      HttpEntity fileEntity = httpResponse.getEntity();
              
                      if (fileEntity != null) {
                          FileUtils.copyInputStreamToFile(fileEntity.getContent(), new File(fileSavePath));
                      }
              
                  } catch (IOException e) {
                      isSucceed = false;
                  }
              
                  httpGet.releaseConnection();
              
                  return isSucceed;
              }
              

              对比单行代码:

              FileUtils.copyURLToFile(fileURL, new File(fileSavePath),
                                      URLS_FETCH_TIMEOUT, URLS_FETCH_TIMEOUT);
              

              此代码可以让您更好地控制进程,让您不仅可以指定超时,还可以指定 User-AgentReferer 值,这对许多网站来说都至关重要。

              【讨论】:

                【解决方案16】:

                这是另一个基于 Brian Risk's answer 的 Java 7 变体,使用了 try-with 语句:

                public static void downloadFileFromURL(String urlString, File destination) throws Throwable {
                
                    URL website = new URL(urlString);
                    try(
                        ReadableByteChannel rbc = Channels.newChannel(website.openStream());
                        FileOutputStream fos = new FileOutputStream(destination);
                       ) {
                
                        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
                    }
                }
                

                【讨论】:

                • 一次调用是不够的。 transferFrom() isnt' 指定在单个调用中完成整个传输。这就是它返回计数的原因。你必须循环。
                • 我不知道你为什么要向我提出这个愚蠢的问题。这与我所说的没有任何关系,我真的拒绝把话放在嘴里。
                【解决方案17】:

                更简单的non-blocking I/O用法:

                URL website = new URL("http://www.website.com/information.asp");
                try (InputStream in = website.openStream()) {
                    Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
                }
                

                【讨论】:

                • 不幸的是,如果出现诸如“302 Found”之类的重定向,这会静默失败(下载 0 个字节)。
                • @AlexanderK 但是你为什么要盲目下载这样的资源呢?
                • 尽管这是一个优雅的解决方案,但在幕后这种方法可能会默默地背叛你。 Files.copy( InputStream, Paths, FileOption) 将复制过程委托给 Files.copy( InputStream, OutputStream )。最后一种方法不检查流的结尾 (-1),但检查是否没有读取字节 (0)。这意味着,如果您的网络稍有停顿,它可以读取 0 字节并结束复制过程,即使操作系统尚未完成下载流。
                • @Miere InputStream.read() 不可能返回零,除非您提供零长度缓冲区或计数、“小暂停”或其他。它将阻塞直到至少一个字节已被传输或流结束或发生错误。你对Files.copy() 内部的说法毫无根据。
                • 我有一个读取 2.6TiB 二进制文件的单元测试。使用 Files.copy 它总是在我的 HDD 存储服务器 (XFS) 上失败,但它只失败了几次我的 SSH 一次。查看 JDK 8 File.copy 的代码,我发现它检查“> 0”以离开“while”循环。我只是用 -1 复制了完全相同的代码,两个单元测试再也没有停止过。一旦 InputStream 可以表示网络和本地文件描述符,并且两个 IO 操作都受操作系统上下文切换的影响,我不明白为什么我的说法是毫无根据的。人们可能会声称它是幸运的,但它不再让人头疼了。
                【解决方案18】:
                import java.io.*;
                import java.net.*;
                
                public class filedown {
                    public static void download(String address, String localFileName) {
                        OutputStream out = null;
                        URLConnection conn = null;
                        InputStream in = null;
                
                        try {
                            URL url = new URL(address);
                            out = new BufferedOutputStream(new FileOutputStream(localFileName));
                            conn = url.openConnection();
                            in = conn.getInputStream();
                            byte[] buffer = new byte[1024];
                
                            int numRead;
                            long numWritten = 0;
                
                            while ((numRead = in.read(buffer)) != -1) {
                                out.write(buffer, 0, numRead);
                                numWritten += numRead;
                            }
                
                            System.out.println(localFileName + "\t" + numWritten);
                        } 
                        catch (Exception exception) { 
                            exception.printStackTrace();
                        } 
                        finally {
                            try {
                                if (in != null) {
                                    in.close();
                                }
                                if (out != null) {
                                    out.close();
                                }
                            } 
                            catch (IOException ioe) {
                            }
                        }
                    }
                
                    public static void download(String address) {
                        int lastSlashIndex = address.lastIndexOf('/');
                        if (lastSlashIndex >= 0 &&
                        lastSlashIndex < address.length() - 1) {
                            download(address, (new URL(address)).getFile());
                        } 
                        else {
                            System.err.println("Could not figure out local file name for "+address);
                        }
                    }
                
                    public static void main(String[] args) {
                        for (int i = 0; i < args.length; i++) {
                            download(args[i]);
                        }
                    }
                }
                

                【讨论】:

                • 如果in.close 抛出异常,out.close 不会被调用。
                【解决方案19】:

                试试Java NIO

                URL website = new URL("http://www.website.com/information.asp");
                ReadableByteChannel rbc = Channels.newChannel(website.openStream());
                FileOutputStream fos = new FileOutputStream("information.html");
                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
                

                使用transferFrom()可能比从源通道读取并写入此通道的简单循环更有效。许多操作系统可以直接将字节从源通道传输到文件系统缓存中,而无需实际复制它们。

                查看更多信息here

                注意:transferFrom 中的第三个参数是要传输的最大字节数。 Integer.MAX_VALUE 最多传输 2^31 字节,Long.MAX_VALUE 最多允许 2^63 字节(比现有的任何文件都大)。

                【讨论】:

                • 使用 Java 7 try-with-resource 关闭所有三个:try (InputStream inputStream = website.openStream(); ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream); FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) { fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, 1
                • @kirdie 如果我想要的不仅仅是8388608 TB?
                • 一次调用是不够的。 transferFrom() isnt' 指定在单个调用中完成整个传输。这就是它返回计数的原因。你必须循环。
                • 为什么这个答案被接受了? URL::openStream()只返回一个常规流,这意味着整个流量仍在通过 Java byte[] 数组复制,而不是保留在本机缓冲区中。只有fos.getChannel()实际上是一个本地频道,所以开销仍然是完整的。在这种情况下,使用 NIO 的收益为零。除了被破坏之外,正如 EJP 和 Ben MacCann 正确注意到的那样。
                【解决方案20】:
                public class DownloadManager {
                
                    static String urls = "[WEBSITE NAME]";
                
                    public static void main(String[] args) throws IOException{
                        URL url = verify(urls);
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        InputStream in = null;
                        String filename = url.getFile();
                        filename = filename.substring(filename.lastIndexOf('/') + 1);
                        FileOutputStream out = new FileOutputStream("C:\\Java2_programiranje/Network/DownloadTest1/Project/Output" + File.separator + filename);
                        in = connection.getInputStream();
                        int read = -1;
                        byte[] buffer = new byte[4096];
                        while((read = in.read(buffer)) != -1){
                            out.write(buffer, 0, read);
                            System.out.println("[SYSTEM/INFO]: Downloading file...");
                        }
                        in.close();
                        out.close();
                        System.out.println("[SYSTEM/INFO]: File Downloaded!");
                    }
                    private static URL verify(String url){
                        if(!url.toLowerCase().startsWith("http://")) {
                            return null;
                        }
                        URL verifyUrl = null;
                
                        try{
                            verifyUrl = new URL(url);
                        }catch(Exception e){
                            e.printStackTrace();
                        }
                        return verifyUrl;
                    }
                }
                

                【讨论】:

                • 您可以通过提供代码如何工作的信息来改进您的答案,而不仅仅是转储它。
                • 并修复它以在出现异常时关闭资源。
                【解决方案21】:

                这里有许多优雅而有效的答案。但是简洁会让我们丢失一些有用的信息。特别是,通常不希望将连接错误视为异常,并且可能希望以不同的方式处理某种与网络相关的错误 - 例如,决定我们是否应该重试下载。

                这是一种不会为网络错误抛出异常的方法(仅适用于真正的异常问题,例如格式错误的 url 或写入文件的问题)

                /**
                 * Downloads from a (http/https) URL and saves to a file. 
                 * Does not consider a connection error an Exception. Instead it returns:
                 *  
                 *    0=ok  
                 *    1=connection interrupted, timeout (but something was read)
                 *    2=not found (FileNotFoundException) (404) 
                 *    3=server error (500...) 
                 *    4=could not connect: connection timeout (no internet?) java.net.SocketTimeoutException
                 *    5=could not connect: (server down?) java.net.ConnectException
                 *    6=could not resolve host (bad host, or no internet - no dns)
                 * 
                 * @param file File to write. Parent directory will be created if necessary
                 * @param url  http/https url to connect
                 * @param secsConnectTimeout Seconds to wait for connection establishment
                 * @param secsReadTimeout Read timeout in seconds - trasmission will abort if it freezes more than this 
                 * @return See above
                 * @throws IOException Only if URL is malformed or if could not create the file
                 */
                public static int saveUrl(final Path file, final URL url, 
                  int secsConnectTimeout, int secsReadTimeout) throws IOException {
                    Files.createDirectories(file.getParent()); // make sure parent dir exists , this can throw exception
                    URLConnection conn = url.openConnection(); // can throw exception if bad url
                    if( secsConnectTimeout > 0 ) conn.setConnectTimeout(secsConnectTimeout * 1000);
                    if( secsReadTimeout > 0 ) conn.setReadTimeout(secsReadTimeout * 1000);
                    int ret = 0;
                    boolean somethingRead = false;
                    try (InputStream is = conn.getInputStream()) {
                        try (BufferedInputStream in = new BufferedInputStream(is); OutputStream fout = Files
                                .newOutputStream(file)) {
                            final byte data[] = new byte[8192];
                            int count;
                            while((count = in.read(data)) > 0) {
                                somethingRead = true;
                                fout.write(data, 0, count);
                            }
                        }
                    } catch(java.io.IOException e) { 
                        int httpcode = 999;
                        try {
                            httpcode = ((HttpURLConnection) conn).getResponseCode();
                        } catch(Exception ee) {}
                        if( somethingRead && e instanceof java.net.SocketTimeoutException ) ret = 1;
                        else if( e instanceof FileNotFoundException && httpcode >= 400 && httpcode < 500 ) ret = 2; 
                        else if( httpcode >= 400 && httpcode < 600 ) ret = 3; 
                        else if( e instanceof java.net.SocketTimeoutException ) ret = 4; 
                        else if( e instanceof java.net.ConnectException ) ret = 5; 
                        else if( e instanceof java.net.UnknownHostException ) ret = 6;  
                        else throw e;
                    }
                    return ret;
                }
                

                【讨论】:

                  【解决方案22】:
                  public void saveUrl(final String filename, final String urlString)
                          throws MalformedURLException, IOException {
                      BufferedInputStream in = null;
                      FileOutputStream fout = null;
                      try {
                          in = new BufferedInputStream(new URL(urlString).openStream());
                          fout = new FileOutputStream(filename);
                  
                          final byte data[] = new byte[1024];
                          int count;
                          while ((count = in.read(data, 0, 1024)) != -1) {
                              fout.write(data, 0, count);
                          }
                      } finally {
                          if (in != null) {
                              in.close();
                          }
                          if (fout != null) {
                              fout.close();
                          }
                      }
                  }
                  

                  您需要处理异常,可能在此方法之外。

                  【讨论】:

                  • 如何下载更快?喜欢下载加速器?
                  • 如果in.close 抛出异常,fout.close 不会被调用。
                  • @ComFreek 这根本不是真的。使用BufferedInputStream 对套接字超时的影响正好为零。我已经在我的 cmets 中将您引用的“背景细节”驳斥为“城市神话”。三年前。
                  • @EJP 谢谢指正!我删除了我的评论(对于存档:我链接到 this answer 声明 BufferedInputStream“可能导致不可预知的故障”)。
                  • +1 我对这个答案(以及这里的其他答案)的唯一反对意见是调用者无法将“未找到”事件与某些连接错误(您可能想重试)区分开来。
                  【解决方案23】:

                  就我个人而言,我发现Apache's HttpClient 能够胜任我在这方面需要做的所有事情。 Here 是一个很好的使用 HttpClient 的教程

                  【讨论】:

                  • commons-io 也是一个很棒的库
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-12-23
                  • 2012-07-05
                  • 1970-01-01
                  • 2012-08-03
                  • 2021-03-19
                  • 2013-08-14
                  相关资源
                  最近更新 更多