单线程下载
String uri = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1489320877777&di=0602ac45c01a727564917f688e3d4ad2&imgtype=0&src=http%3A%2F%2Fpic41.nipic.com%2F20140521%2F18810499_233249416000_2.jpg";
// 构造URL
URL url = new URL(uri);
// 打开连接
URLConnection con = url.openConnection();
// 输入流
InputStream is = con.getInputStream();
// 1K的数据缓冲
byte[] bs = new byte[1024];
// 读取到的数据长度
int len;
File f = new File("F:\\a.jpg");
// 输出的文件流
OutputStream os = new FileOutputStream(f);
// 开始读取 ,每次不能确定读取多少个字节
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
// 完毕,关闭所有链接
os.close();
is.close();
从网络上下载的文件read(byte[]) 无法保证每次读取多少个字节,这儿可能是根据流量分的,从本地读取的时候 可以保证每次能读取到的字节数就是1024,所以可以设置数组长度为图片大小os.available(),j就不用while了
多线程下载
好处:可以一边下载一边往文件里写入数据,不用等待?提问?问啥要用多线程呀
ConnectionManagerImpl 用于打开连接
public class ConnectionManagerImpl implements ConnectionManager {
@Override
public Connection open(String url) throws ConnectionException {
try {
URL uri = new URL(url);
HttpURLConnection conn = (HttpURLConnection) uri.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(1000);
Connection connImpl = new ConnectionImpl(conn);
return connImpl;
} catch (IOException e) {
// TODO Auto-generated catch block
}
return null;
}
}
ConnectionImpl 用于得到字节数,读取某段字节
public class ConnectionImpl implements Connection{
private HttpURLConnection conn;
private InputStream is;
public ConnectionImpl(HttpURLConnection conn) {
try {
this.conn = conn;
is = conn.getInputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public byte[] read(int startPos, int endPos) throws IOException {
if(is==null){
return null;
}
is.skip(startPos);
int len = endPos-startPos;
byte[] bt = new byte[len];
byte[] temp = new byte[1024];
int m=0;
int nowLen=0;
while((m=is.read(temp))>0){
if(nowLen+m>len){
System.arraycopy(temp, 0, bt, nowLen, len-nowLen);
break;
}
System.arraycopy(temp, 0, bt, nowLen, m);
nowLen += m;
}
return bt;
}
@Override
public int getContentLength() {
if(conn==null){
return 0;
}
try {
int length=conn.getContentLength();
return length;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return 0;
}
@Override
public void close() {
if(is!=null){
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
下载 DownLoadThread
public class DownloadThread extends Thread{
Connection conn;
int startPos;
int endPos;
byte[] reads;
public DownloadThread( Connection conn, int startPos, int endPos){
this.conn = conn;
this.startPos = startPos;
this.endPos = endPos;
}
public void run(){
try {
reads=conn.read(startPos, endPos);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
多线程下载 DownloadThreadImpl
public class FileDownloader {
String url;
DownloadListener listener;
ConnectionManager cm;
public FileDownloader(String _url) {
this.url = _url;
}
public void execute(){
// 在这里实现你的代码, 注意: 需要用多线程实现下载
// 这个类依赖于其他几个接口, 你需要写这几个接口的实现代码
// (1) ConnectionManager , 可以打开一个连接,通过Connection可以读取其中的一段(用startPos, endPos来指定)
// (2) DownloadListener, 由于是多线程下载, 调用这个类的客户端不知道什么时候结束,所以你需要实现当所有
// 线程都执行完以后, 调用listener的notifiedFinished方法, 这样客户端就能收到通知。
// 具体的实现思路:
// 1. 需要调用ConnectionManager的open方法打开连接, 然后通过Connection.getContentLength方法获得文件的长度
// 2. 至少启动3个线程下载, 注意每个线程需要先调用ConnectionManager的open方法
// 然后调用read方法, read方法中有读取文件的开始位置和结束位置的参数, 返回值是byte[]数组
// 3. 把byte数组写入到文件中
// 4. 所有的线程都下载完成以后, 需要调用listener的notifiedFinished方法
// 下面的代码是示例代码, 也就是说只有一个线程, 你需要改造成多线程的。
Connection conn1 = null;
Connection conn2 = null;
Connection conn3 = null;
try {
File f = new File("F:\\百度.jpg");
FileOutputStream os=null;
try {
if(!f.exists()){
f.createNewFile();
}else{
f.delete();
}
os = new FileOutputStream(f,true);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
conn1 = cm.open(this.url);
int length = conn1.getContentLength();
conn2 = cm.open(this.url);
conn3 = cm.open(this.url);
DownloadThread thread1=new DownloadThread(conn1,0,length/3);
DownloadThread thread2=new DownloadThread(conn2,length/3,(length/3)*2);
DownloadThread thread3 =new DownloadThread(conn3,(length/3)*2,length-1);
thread1.start();
thread2.start();
thread3.start();
StringBuilder add = new StringBuilder();
while(true){
if(!thread1.isAlive()&&add.indexOf("1")<0){
try {
os.write(thread1.reads);
add.append("1");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(!thread2.isAlive()&&add.indexOf("2")<0&&add.indexOf("1")==0){
try {
os.write(thread2.reads);
add.append("2");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(!thread3.isAlive()&&add.indexOf("3")<0&&add.indexOf("12")==0){
try {
os.write(thread3.reads);
add.append("3");
os.flush();
os.close();
listener.notifyFinished(true);
break;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} catch (ConnectionException e) {
e.printStackTrace();
}finally{
if(conn1 != null){
conn1.close();
}
if(conn2!=null){
conn2.close();
}
if(conn3!=null){
conn3.close();
}
}
}
public void setListener(DownloadListener listener) {
this.listener = listener;
}
public void setConnectionManager(ConnectionManager ucm){
this.cm = ucm;
}
public DownloadListener getListener(){
return this.listener;
}
}
测试
public void testDownload() {
String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1489320877777&di=0602ac45c01a727564917f688e3d4ad2&imgtype=0&src=http%3A%2F%2Fpic41.nipic.com%2F20140521%2F18810499_233249416000_2.jpg";
FileDownloader downloader = new FileDownloader(url);
ConnectionManager cm = new ConnectionManagerImpl();
downloader.setConnectionManager(cm);
DoloadListenerImpl lister = new DoloadListenerImpl();
downloader.setListener(lister);
downloader.execute();
// 等待多线程下载程序执行完毕
while (!downloader.getListener().getIsFinished()) {
try {
System.out.println("还没有下载完成,休眠五秒");
//休眠5秒
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("下载完成!");
}