如何使用Java从Internet下载和保存文件?


Answers:


559

提供的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()可能不是一个简单的循环从源信道的读取和写入这个频道有效得多。许多操作系统可以直接将字节从源通道传输到文件系统缓存中,而无需实际复制它们。

在这里查看更多信息。

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


22
使用Java 7 try-with-resource关闭全部三个:try(InputStream inputStream = website.openStream(); ReadableByteChannel可读ByteChannel = Channels.newChannel(inputStream); FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)){fileOutputStream.getChannel()。transferFrom (readerByteChannel,0,1 << 24); }
mazatwork 2012年


31
@kirdie,如果我想获得更多8388608结核病?
Cruncher 2013年

22
一个电话是不够的。transferFrom()指定为“ isnt”以在单个呼叫中完成整个转移。这就是为什么它返回一个计数。你必须循环。
洛恩侯爵,

11
为什么这个答案甚至被接受?URL::openStream()仅返回常规流,这意味着整个流量仍将通过Java byte []数组进行复制,而不是保留在本机缓冲区中。fos.getChannel()实际上只有一个本机通道,因此开销保持完整。在这种情况下,使用NIO的收益为零。正如EJP和Ben MacCann正确注意到的那样,除了被打破之外。
Ext3h 2015年

494

使用apache commons-io,只需一行代码:

FileUtils.copyURLToFile(URL, File)

22
真好!正是我想要的!我知道Apache库已经涵盖了这一点。顺便说一句,建议使用带有超时参数的重载版本!
Hendy Irawan 2012年

6
...并且使用该重载版本时,请记住,超时是以毫秒为单位而不是秒。
拉斯洛·范登霍克

5
请注意,copyURLToFile仅从Commons IO库的2.0版开始,with timeout参数才可用。查看Java文档
Stanley

6
如果必须将基本身份验证标头添加到请求中怎么办?有解决方法吗?
达米安2015年

3
尽管这很“短”,但实际上非常慢。
Meinkraft 2015年

123

Nio的用法更简单:

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

5
不幸的是,如果存在诸如“ 302 Found”之类的重定向,此操作将无提示地失败(下载0个字节)。
亚历山大·K

1
@AlexanderK但是,为什么您仍然会盲目下载这样的资源?
xuesheng

5
尽管事实上这是一个优雅的解决方案,但这种方法在后台可能会悄悄地背叛您。Files.copy(InputStream,Paths,FileOption)将复制过程委托给Files.copy(InputStream,OutputStream)。最后一种方法不检查流的末尾(-1),而是检查不读取字节(0)。这意味着,如果您的网络稍有停顿,它就可以读取0个字节并结束复制过程,即使该流尚未完成由OS下载的速度。
Miere

6
@Miere InputStream.read()除非提供了长度为零的缓冲区或计数,“小暂停”或其他方式,否则不可能返回零。它将阻塞,直到已传输至少一个字节或流结束或发生错误为止。您对的内部说法Files.copy()是毫无根据的。
罗恩侯爵,

3
我有一个单元测试,可以读取2.6TiB的二进制文件。使用Files.copy,它在我的HDD存储服务器(XFS)上总是会失败,但它只会失败几倍于SSH。查看JDK 8,我发现File.copy的代码已经检查出它是否检查“> 0”以离开“ while”循环。我只是复制了与-1完全相同的代码,并且两个单元测试再也没有停止过。一旦InputStream可以表示网络和本地文件描述符,并且两个IO操作都需要进行OS上下文切换,那么我看不到为什么我的主张是毫无根据的。可能有人声称它是靠运气工作的,但它再也没有让人头疼了。
Miere

85
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();
        }
    }
}

您需要处理可能在此方法外部的异常。


6
如何下载得更快?喜欢下载加速器吗?
digz6666

11
如果in.close抛出异常,fout.close则不调用。
铍2013年

1
@ComFreek这根本是不正确的。使用a BufferedInputStream对套接字超时的影响为零。在我对您引用的“背景细节”的评论中,我已经将其视为“城市神话”。三年前
罗恩侯爵

@EJP谢谢您的指正!我删除了我的评论(对于归档文件:我链接到此答案,指出BufferedInputStream“可能导致不可预测的失败”)。
ComFreek 2014年

+1我对此答复(以及此处的其他答复)的唯一异议是,调用者无法将“未找到”事件与某些连接错误(可能要重试)区别开来。
leonbloy

24

这是一个古老的问题,但这是一个简洁,易读,仅JDK的解决方案,具有适当封闭的资源:

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

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


对于想知道的人,这里是此代码段所需的导入:import java.io.InputStream; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths;
BelovedFool

23

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

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);
    }

17

使用时,请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;
}

文档在这里


16

这个答案几乎与选择的答案完全一样,但是有两个增强:这是一个方法,它关闭了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();
        }
    }

2
一个电话是不够的。transferFrom()指定为“ isnt”以在单个呼叫中完成整个转移。这就是为什么它返回一个计数。你必须循环。
罗恩侯爵

10
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]);
        }
    }
}

5
如果in.close抛出异常,out.close则不调用。

8

就个人而言,我发现Apache的HttpClient不仅具有处理我需要做的所有事情的能力。 是有关使用HttpClient的出色教程


2
也是commons-io是一个很棒的图书馆
dfa

6

这是另一个基于Brian Risk的 try-with语句的java7变体:

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”以在单个呼叫中完成整个转移。这就是为什么它返回一个计数。你必须循环。
罗恩侯爵

我不知道你为什么要向我解决这个愚蠢的问题。这与我所说的没有任何关系,我真的拒绝在我嘴里说些什么。
罗恩侯爵

2

可以使用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值,这对于许多Web站点的关键。


2

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

这是一种不会因网络错误而引发异常的方法(仅适用于真正异常的问题,例如格式错误的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;
}

2

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

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);
    }

通常,如果答案包括对代码意图的解释,以及为什么不引入其他代码就能解决问题的原因,则答案会更有帮助。
蒂姆·迪克曼

1

简单使用存在一个问题:

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();
}

1

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

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 dir 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 dir 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 dir 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();
}

我没有发现重大差异,所有这些对我来说似乎都是正确的。他们是安全和有效的。(速度差异似乎无关紧要-我将本地服务器的180Mb写入SSD磁盘的时间大约在1.2到1.5段之间波动)。他们不需要外部库。所有这些都可以使用任意大小和(以我的经验)HTTP重定向来工作。

此外,FileNotFoundException如果找不到资源(通常为错误404),则会抛出所有异常,并且java.net.UnknownHostException DNS解析失败则会。其他IOException对应于传输期间的错误。

(标记为社区Wiki,随时添加信息或更正)


1

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

pom.xml:

  <groupId>com.github.javadev</groupId>
  <artifactId>underscore</artifactId>
  <version>1.45</version>

代码示例:

import com.github.underscore.lodash.U;

public class Download {
    public static void main(String ... args) {
        String text = U.fetch("https://stackoverflow.com/questions"
        + "/921262/how-to-download-and-save-a-file-from-internet-using-java").text();
    }
}

当链接无效时,此答案有多有用?请查看如何回答
JimHawkins

您的代码将无法编译。问题在中寻求解决方案Java,但您的答案像JavaScript
talex

@talex我添加了pom.xml部分并改进了代码示例。
Valentyn Kolesnikov

0
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;
    }
}

您可以通过提供信息而不是仅转储代码的方式来改善您的答案。
Matej Kormuth'7

0

您可以使用netloader for Java在1行中完成此操作:

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

0

如果您在代理后面,则可以在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("Io exception");
            e.printStackTrace();
        }
        finally{
            if(outStream!=null) outStream.close();
        }
    }

0

使用新频道的第一种方法

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上搜索以获取详细信息和其他选项。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.