I/O就是指应用程序对数据存储设备的数据输入和输出。
File类
Java语言对物理存储介质中的文件和目录进行了抽象,使用java.io.File类来代表存储介质中的文件和目录。
File类无法访问文件的具体内容,既不能从文件中读数据,也不能向文件中写数据。
构造器:
public File(String pathname):传入路径名。可以是相对路径,也可以是绝对路径。如果是相对路径,则是相对于操作系统的“user.dir”系统属性所指定的路径。
常用属性:
public static final String separator:存储了当前系统的路径分隔符。Linux、unix中为“/”,Windows中为“\”。
访问文件属性的方法
public boolean canRead() | 判断是否可读 |
public boolean canWrite() | 判断是否可写 |
public boolean exists() | 判断文件是否存在 |
public boolean isDirectory() | 判断是否为目录 |
public boolean isFile() | 判断是否为文件 |
public boolean isHidden() | 判断是否隐藏 |
public long lastModified() | 返回最后修改时间(毫秒值) |
public long length() | 返回文件长度(以字节为单位) |
public String getName() | 返回文件名 |
public String getPath() | 返回文件目录 |
public String getAbsolutePath() | 返回该文件的绝对 路径 |
public String getCanonicalPath() | 返回该文件的规范路径名 |
public File getAbsoluteFile() | 得到绝对路径规范表示的文件对象 |
public String getParent() | 得到该文件的父目录路径名 |
public URI toURI() | 返回该文件的统一资源标识符名 |
文件的操作
public boolean createNewFile() | 不存在时,创建此文件对象所代表的空文件 |
public boolean delete() | 删除文件(如果是目录,空目录才可以删除) |
public boolean mkdir() | 创建此抽象路径名指定的目录 |
public boolean mkdirs() | 创建此抽象路径名指定的目录,包括所有必需但又还不存在的父目录 |
public boolean renameTo(File dest) | 重命名此抽象路径名表示的文件 |
浏览目录中文件和子目录的方法
public String[] list() | 返回此目录中的文件名和目录名的数组 |
public File[] listFiles() | 返回此目录中的文件名和目录名的File实例数组 |
public File[] listFiles(FilenameFilter filter) | 返回目录中满足指定文件过滤器的文件和目录 |
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
File file=new File("F:\\FileTest\\src.txt");
System.out.println("文件是否存在:"+file.exists());
System.out.println("文件长度:"+file.length()+"字节");
System.out.println("文件最后修改时间:"+file.lastModified());
File f=new File("F:\\FileTest");
File f1=new File("F:\\FileTest\\f1");
if(!f1.exists()){
f1.mkdir();
}
File f2=new File("F:\\FileTest\\f\\f2");
if(!f2.exists()){
f2.mkdirs();
}
File f3=new File("F:\\FileTest\\f\\test.txt");
if(!f3.exists()){
f3.createNewFile();
}
File f4=new File("F:\\FileTest\\f\\f2\\test.txt");
if(!f4.exists()){
f4.createNewFile();
}
System.out.println("f4的绝对路径名:"+f4.getAbsolutePath());
listChilds(f,0);
deleteAll(f);
}
private static void deleteAll(File file) {
// TODO Auto-generated method stub
//如果file为文件
if(file.isFile()){
System.out.println("删除文件"+file.getAbsolutePath());
file.delete();
return;
}
//如果file为目录 先删除子目录以及所有文件
File[] lists=file.listFiles();
for(int i=0;i<lists.length;i++){
deleteAll(lists[i]);//递归删除
}
System.out.println("删除目录:"+file.getAbsolutePath());
file.delete();
}
//递归显示指定目录下的文件和目录信息
private static void listChilds(File dir, int level) { //level记录递归层次
// TODO Auto-generated method stub
StringBuilder sb=new StringBuilder("|--");
for(int i=0;i<level;i++){
sb.insert(0, "| ");
}
File[] childs=dir.listFiles();
//递归出口
int length=(childs==null)?0:childs.length;
for(int i=0;i<length;i++){
System.out.println(sb.toString()+childs[i].getName());
if(childs[i].isDirectory()){
listChilds(childs[i], level+1);
}
}
}
}
运行结果:
文件是否存在:true
文件长度:18字节
文件最后修改时间:1523177614372
f4的绝对路径名:F:\FileTest\f\f2\test.txt
|--f
| |--f2
| | |--test.txt
| |--test.txt
|--f1
|--src.txt
删除文件F:\FileTest\f\f2\test.txt
删除目录:F:\FileTest\f\f2
删除文件F:\FileTest\f\test.txt
删除目录:F:\FileTest\f
删除目录:F:\FileTest\f1
删除文件F:\FileTest\src.txt
删除目录:F:\FileTest
I/O流
按数据流向分为:输入流和输出流。
按数据传输单位分:字节流和字符流
按流的功能:节点流和处理流。节点流是直接操作目标设备的流,处理流是对一个流的处理和封装,功能更强大。
java.io中的流类都继承四个抽象流:
字节流 | 字符流 | |
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
InputStream
public abstract int read() throws IOException | 从输入流中读取数据的下一个字节,返回读到的字节值。如果流到末尾,返回-1。 |
public int read(byte[] b)throws IOException | 从输入流中读取b.length个字节的数据并存储到缓存区数组b中,返回的是实际读到的字节数 |
public int read(byte[] b,int off,int len)throws IOException | 读取len个字节的数据,并从数组b的off位置开始写入到这个数组 |
public void close()throws IOException | 关闭此输入流,并释放所有与此输入流相关的系统资源 |
public int available()throws IOException | 返回此输入流下一个方法调用可以不受堵塞地从输入流读取或跳过的估计字数 |
public skip(long n)throws IOException | 跳过和丢弃此输入流的n个字节,返回实现跳过的字节数 |
OutputStream
public abstract void write(int b)throws IOException | 将指定的字节写入此输出流 |
public void write(byte[] b)throws IOException | 将b.leng个字节从指定的byte数组写入此输出流 |
public void write(byte[] b,int off,int len)throws IOException | 将指定byte数组中从偏移量off开始的len个字节写入此输出流 |
public void flush()throws IOException | 刷新此输出流,并强制写出所有输出的输出字节 |
public void close()throws IOException | 关闭此输出流,并释放所有与此输出流相关的系统资源 |
Reader
public int read()throws IOException | 读取单个字符,返回作为整数读取的字符,如果已经到达流尾,返回-1 |
public int read(char[] ch)throws IOException | 将字符读入数组ch,返回读到的字符数。 |
public abstract int read(char[] ch,int len)throws IOException | 读取len个字符的数据,并从数组ch的off位置开始写入到这个数组 |
public abstract void close()throws IOException | 关闭该流并释放与之相关的所有资源 |
public long skip(long n)throws IOExveption | 跳过n个字符 |
Write
public void write(int c)throws IOException | 输出单个字符 |
public void write(char[] ch)throws IOException | 输出字符数组 |
public abstract void write(char[] ch,int off,int len)throws IOException | 输出字符数组的某一部分 |
public void write(String str)throws IOException | 输出字符串 |
public void write(String str,int off,int len)throws IOException | 输出字符串的某一部分 |
public abstract void close()throws IOException | 关闭此流(要先刷新) |
public abstract void flush()throws IOException | 刷新该流的缓冲,并将缓冲数据全部写到目的地 |
文件流
FileInputStream和FileOutputStream
使用FileInputStream类来读取指定文件的数据
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class FileInputStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream fin=null;
InputStreamReader isr=null;
try {
fin=new FileInputStream("F:\\Test\\test.txt");
// System.out.println("可读取的字节数:"+fin.available());
// for(int b=-1;(b=fin.read())!=-1;){
// //int->char 向下转型 强转
// System.out.print((char)b);//输出 Hello World!?????? 出现乱码
// }
// for(int b=-1;(b=isr.read())!=-1;){
// System.out.print((char)b);//输出 Hello World!啦啦啦
// }
//通过转换流解决 将字节流转换成字符流
isr=new InputStreamReader(new FileInputStream("F:\\Test\\test.txt"),"GBK");//GBK编码集
char buff[]=new char[1024];//定义一个字符数组 每次读1024个字符
for(int b=-1;(b=isr.read(buff))!=-1;){
System.out.println(new String(buff,0,b));//输出Hello World!啦啦啦
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally{
if(null!=fin){
try {
fin.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=isr){
try {
isr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
运行结果:
Hello World!啦啦啦
使用FileOutputStream类往指定文件中写入数据
import java.io.*;
public class FileOutStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileOutputStream out=null;
try {
out=new FileOutputStream("F://Test//OutTest.txt",true);//第二个参数设置为true,表示使用追加模式来添加字符
out.write('#');
out.write("Hello World".getBytes());//字符串转换为字节写入
out.write("你好".getBytes());
out.flush();//刷新输出流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
运行结果:
在指定目录创建OutTest.txt文件,并将字符写入其中 #Hello World你好
I/O流操作文件步骤:
1.创建指定数据源的I/O流对象
2.利用I/O流进行读写输入。如果是写入数据,还要调用flush()方法刷新
3.调用close()方法关闭对象
import java.io.*;
public class CopyPictureTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileInputStream is=null;
FileOutputStream os=null;
try{
is=new FileInputStream("F:\\Test\\kof97.bmp");
os=new FileOutputStream("F:\\zz.bmp");
// //一个字符一个字符的去读和写
// for(int b=-1;(b=is.read())!=-1;){
// os.write(b);
// }
// os.flush();
// char[] buff=new char[1024];
byte[] buff=new byte[1024];//字节流 所以定义字节数组
for(int b=-1;(b=is.read(buff))!=-1;){
os.write(buff, 0, b);
}
os.flush();
}catch(IOException e){
e.printStackTrace();
}finally{
if(null!=is){
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=os){
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
FileReader和FileWrite
多用于操作字符文本文件
package fileInOut;
import java.io.*;
public class TextCopyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
FileReader fr=null;
FileWriter fw=null;
FileInputStream fi=null;
FileOutputStream fo=null;
try{
//创建I/O对象
fr=new FileReader("F://test//fr.txt");
fw=new FileWriter("F://test//fw.txt");
fi=new FileInputStream("F://test//fr.txt");
fo=new FileOutputStream("F://test//fo.txt");
//I/O流操作
char[] c=new char[1024];//创建字符数组
for(int i=-1;(i=fr.read(c))!=-1;){
fw.write(c,0,i);
}
byte[] b=new byte[1024];//创建字节数组
for(int i=-1;(i=fi.read(b))!=-1;){
fo.write(b,0,i);
}
fw.flush();
fo.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
if(null!=fr){
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=fw){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=fi){
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=fo){
try {
fo.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
用两种I/O测试的目的是,是否有中文乱码的问题和文件大小是否变化的问题,测试完发现:没有出现这两个问题。
缓冲流
提高数据的读写速度、将读/写数据放入缓冲区,然后读/写,因此缓冲流不能直接读/写数据,而是对已知流的包装。
BufferedInputStream:针对字节输入流的缓冲流类
BufferedOutputStream:针对字节输入流的缓冲流类
BufferedReader:针对字节输入流的缓冲流类
BufferedWrite:针对字节输入流的缓冲流类
import java.io.*;
public class BufferedTestCopyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
BufferedReader bi=null;
BufferedWriter bo=null;
try{
//创建缓冲流对象,对字节流的包装
bi=new BufferedReader(new FileReader("F://test//fi.txt"));
bo=new BufferedWriter(new FileWriter("F://test//fo.txt"));
//如果fo.txt文件有内容,会清空,fo.txt就是fi.txt的拷贝,唯一不同的是文件名
for(String str=null;(str=bi.readLine())!=null;){
bo.write(str);
bo.newLine();//写入行分隔符
}
bo.flush();
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
if(null!=bi){
try {
bi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(null!=bo){
try {
bo.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
转换流
InputStreamReader类用于把字节流转换成字符流,需要与InputStream对象嵌套。两种构造方法:
public InputStreamReader(InputStream in):创建一个使用默认字符集的InputStreamReader对象
public InputStreamReader(InputStream in,String charsetname):创建一个使用指定字符集的InputStreamReader对象
OutputStreamWrite类用于把字符流转换成字节流,需要与OutputStream嵌套。两种构造方法:
public OutputStreamWrite(OutputStream out);//创建一个使用默认字符编码的OutputStreamWrite对象
public OutputStreamWrite(OutputStream out,String charsetname);//创建一个指定字符编码的OutputStreamWrite对象
import java.io.*;
public class ByteToCharTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("请输入信息(退出输入“e”或“exit”");
BufferedReader br=null;
try {
//System.in为标准字节输入流,先转换为字符流,然后包装为缓冲流
br=new BufferedReader(new InputStreamReader(System.in, "GBK"));
for(String str=null;(str=br.readLine())!=null;){
if(str.equalsIgnoreCase("e")||str.equalsIgnoreCase("exit")){ //这里换成str=="e" 没用
System.out.println("安全退出!");
break;
}else{
System.out.println("--->"+str.toUpperCase());//转换为大写
System.out.println("继续输入信息");
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(null!=br){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
import java.io.*;
public class CharToByteTest {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
// FileOutputStream bo=null;
// FileInputStream bi=null;
OutputStreamWriter co=null;
InputStreamReader ci=null;
try {
// bi=new FileInputStream("F://test//fi.txt");
// ci=new InputStreamReader(bi);
// bo=new FileOutputStream("F://test//fo.txt");
// co=new OutputStreamWriter(bo);
//字节流转为字符流
ci=new InputStreamReader(new FileInputStream("F://test//fi.txt"));
co=new OutputStreamWriter(new FileOutputStream("F://test//fo.txt"));
char[] c=new char[1024];
for(int i=-1;(i=ci.read(c))!=-1;){
co.write(c);
}
co.flush();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(null!=ci){
ci.close();
}
if(null!=co){
co.close();
}
}
}
}
数据流
更方便操作Java中的基本数据类型,主要有两类:DataInputStream和DataOutputStream。
DatainputStream操作方法:
public final boolean readBoolean() | 从输入流中读取一个布尔类型 |
public final byte readByte() | 从输入流中读取一个8位的字节 |
public final char readChare() | 从输入流中读取一个16位的Unicode字符 |
public final float readFloat() | 读取一个32位单精度浮点数 |
public final double readDouble() | 读取一个64位双精度浮点数 |
public final short readShort() | 读取一个16位的短整数 |
public final int readInt() | 读取一个32位的整数 |
public final long readLong() | 读取一个64位的长整数 |
public final byte readFully(byte[] b) | 将字节读取到一个字节数组,直到充满整个字节数组 |
public final byte readFully(byte[] b,int off,int len) | |
public final String readUTF() | 读取一个由UTF格式字符组成的字符串 |
public intskipBypes(int n) | 跳过n字节 |
import java.io.*;
public class DataOutputStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
DataInputStream dis=null;
DataOutputStream dos=null;
try {
dos=new DataOutputStream(new FileOutputStream("F://test//do.txt"));
dos.writeUTF("ab中国");
dos.writeBoolean(false);
dos.writeLong(1234567890L);
System.out.println("写文件成功!");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(null!=dos){
try {
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
try {
dis=new DataInputStream(new FileInputStream("F://test//do.txt"));
System.out.println(dis.readUTF());
System.out.println(dis.readBoolean());
System.out.println(dis.readLong());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
dis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
运行结果:
写文件成功!
ab中国
false
1234567890
打印流
包括PrintStream和PrintWriter,输出操作永远不会抛出IOException异常。
import java.io.*;
public class PrintStreamTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建文件输出流
FileOutputStream fos=null;
try {
fos=new FileOutputStream("F://test//text.txt");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//创建打印流
PrintStream ps=new PrintStream(fos, true);
if(ps!=null){
//把标准输出流改成指定的打印输出流
System.setOut(ps);
}
for(int i=0;i<=255;i++){
System.out.print((char)i);
if(i%50==0){
System.out.println();
}
}
}
}
printf()方法提供程序的标准格式化输出
public PrintStream printf(String format,Object ... args) //format参数指定格式字符串;args可变参数是格式字符串中的格式说明符引用的参数值。
格式字符 | 描述 |
%s | 将参数值格式化为字符 |
%d%o%x | 将整数参数值格式化为十进制、八进制、十六进制 |
%f%g | 格式化浮点数 %g为科学计数法 |
%n | 将换行符插入字符串或流 |
%% | 将百分比符号插入字符串或流 |
public class PrintfTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String name="zz";
int age=65;
double salary=23.05;
System.out.printf("姓名:%s,年龄:%d,月薪:%g",name,age,salary);
}
}
运行结果:
姓名:zz,年龄:65,月薪:23.0500
对象流
ObjectOutputStream和ObjectInputStream用于存储和读取基本类型数据或对象的处理流,可以把java中的对象写入文件,也可以从文件中读取对象。用ObjectOutputStream类保存基本类型数据或对象的机制叫做序列化;用ObjectInputStream类读取基本类型数据或对象的机制叫做反序列化。
序列化和反序列化操作
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
能被序列化的类必须实现java.io.Serializable接口,该接口没有方法,仅作为标记。
static或transient修饰的对象不会被序列化,序列化的好处在于,它可以将任何实现Serializable接口的对象转换为字节数据,这些数据可以保存在文件中。
public class Student implements java.io.Serializable{//能被序列化的类必须实现java.io.Serializable接口
/**
*
*/
private static final long serialVersionUID = -265596065438482181L;//序列化的版本标识
private int ID;
private String name;
private transient int age;//表示该对象不需序列化
private static String classID;//该属性不会被序列化
public Student(int ID,String name,int age){
classID="java007";
this.ID=ID;
this.name=name;
this.age=age;
}
public int getId() {
return ID;
}
public void setId(int id) {
ID = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static String getClassID() {
return classID;
}
public static void setClassID(String classID) {
Student.classID = classID;
}
@Override
public String toString() {
return "Student [ID=" + ID + ", name=" + name + ", age=" + age + ", classID="+classID+"]";
}
}
序列化
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializationTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("F://Test//objectSeri.dat"));
oos.writeObject(new Student(101, "张三", 22));
oos.flush();
System.out.println("序列化成功!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(null!=oos){
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
反序列化
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializationTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(new FileInputStream("F://Test//objectSeri.dat"));
try {
Student stu=(Student) ois.readObject();
System.out.println(stu.toString());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(null!=ois){
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
运行结果:
Student [ID=101, name=张三, age=0, classID=null]
age值和classID值丢了 因为创建类时声明这两个对象无需序列化
随机存取文件流
RandomAccessFile是一种特殊的流类,它可以在文件任何地方读取或写入数据。
使用多线程下载网络资源并保存到本地磁盘。
基本思想:获取网络资源的总长度,把网络资源按线程数分成块,每块数据由线程下载;每块下载的资源,都用RandomAccessFile流写到指定文件中。
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MultiThreadDownloadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String urlStr="http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fi7.hexun.com%2F2018-04-22%2F192880585.jpg&thumburl=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D1622885848%2C750396372%26fm%3D11%26gp%3D0.jpg";//资源名
try {
URL url=new URL(urlStr);//创建URL(统一资源标识符)
URLConnection con=url.openConnection();//建立连接
int contentLen=con.getContentLength();//获取资源总长度
int threadQut=5;//线程个数
int subLen=contentLen/threadQut;//每个线程下载大小
int remainder=contentLen%threadQut;//余数
File destFile=new File("F://Test//xiaozhupeiqi.png");
for(int i=0;i<threadQut;i++){
int start=subLen*i;//线程下载资源开始位置
int end=start+subLen-1;//从0开始,所以要-1
if(i==threadQut-1){//线程编码从0到4
end=end+remainder;
}
Thread t=new Thread(new DownloadRunnable(start,end,url,destFile));
t.start();
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class DownloadRunnable implements Runnable{
private final int start;//开始位置
private final int end;//结束位置
private final URL url;//资源标识符
private final File destFile;//指定文件
private static final int BUFFER_SIZE=8192;//缓冲区大小
public DownloadRunnable(int start, int end, URL url, File destFile) {
// TODO Auto-generated constructor stub
this.start=start;
this.end=end;
this.url=url;
this.destFile=destFile;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName()+"开始启动...");
BufferedInputStream bis=null;//输入流缓冲区
RandomAccessFile ras=null;//随机存文件流
byte []bte=new byte[BUFFER_SIZE];//创建字节数组
URLConnection con=null;
try {
con=url.openConnection();//建立网络连接
con.setRequestProperty("Range","bytes="+start+"-"+end);//设置连接的请求头字段:获取资源数据的范围从star到end
bis=new BufferedInputStream(con.getInputStream());//网络连接中获取输入流并包装成缓冲流
ras=new RandomAccessFile(destFile, "rw");//创建随机存取文件流,并指定权限(同时读写)
ras.seek(start);//把文件指针移动到start位置
int len=-1;//读取到的字节数
while((len=bis.read(bte))!=-1){//从网络中读取资源
ras.write(bte,0,len);//用随机存取文件流写入目标文件
}
System.out.println(Thread.currentThread().getName()+"以下载完");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
Thread-0开始启动...
Thread-1开始启动...
Thread-2开始启动...
Thread-4开始启动...
Thread-3开始启动...
Thread-1以下载完
Thread-0以下载完
Thread-2以下载完
Thread-4以下载完
Thread-3以下载完