我需要抓取并保存到目录的是一个在线文件(例如http://www.example.com/information.asp
)。我知道有几种逐行读取在线文件(URL)的方法,但是否有一种使用Java仅下载和保存文件的方式?
我需要抓取并保存到目录的是一个在线文件(例如http://www.example.com/information.asp
)。我知道有几种逐行读取在线文件(URL)的方法,但是否有一种使用Java仅下载和保存文件的方式?
尝试使用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个字节(大于任何现有的文件)。
try (InputStream inputStream = website.openStream();
ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) {
fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, 1 << 24);
}
- mazatworktransferFrom()
没有指定完成整个转账需要一次调用。这就是它返回计数的原因。你需要循环执行。 - user207421URL::openStream()
只返回一个常规的流,这意味着整个流量仍然通过Java byte[]数组复制,而不是保留在本地缓冲区中。只有fos.getChannel()
才是真正的本地通道,因此开销仍然存在。在这种情况下,使用NIO没有任何好处。正如EJP和Ben MacCann正确指出的那样,除了出现故障。 - Ext3h使用Apache Commons IO 库。只需要一行代码:
FileUtils.copyURLToFile(URL, File)
更简单的非阻塞I/O使用方法:
URL website = new URL("http://www.website.com/information.asp");
try (InputStream in = website.openStream()) {
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
}
InputStream.read()
不可能返回零。它会一直阻塞,直到至少传输一个字节,或者流结束或出现错误。您关于Files.copy()
内部工作原理的说法是毫无根据的。 - user207421public 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
将不会被调用。 - BerylliumBufferedInputStream
对套接字超时没有任何影响。我已经在您引用的“背景细节”评论中驳斥了这一点作为“城市传说”。早在三年前。 - user207421MalformedURLException
extends IOException
, so you only have to throw the IOException
- Alex Jone这是一个简洁易读的解决方案,仅使用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));
}
}
只需要两行代码,且不需要任何依赖。
这里有一个完整的文件下载示例程序,包括输出、错误检查和命令行参数检查:
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存储库的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
下载文件需要先读取它。不管怎样,您都必须以某种方式查看文件。而不是逐行读取,您可以从流中按字节读取:
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);
}
如果使用Java 7及以上版本,可以使用以下方法从互联网下载文件并将其保存到某个目录中:
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;
}
文档在这里。
StandardCopyOption
来重命名文件而不是替换现有文件。 - Sridhar Sarnobat这个答案与被选中的答案几乎完全相同,但是它有两个增强功能:它是一种方法,并且关闭了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()
没有规定要在单个调用中完成整个转移。这就是为什么它会返回一个计数值。你需要循环操作。 - user207421import 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
。 - Beryllium就我个人而言,我发现Apache's HttpClient已足够胜任与此相关的所有操作。这里有一个使用HttpClient的优秀教程。