本文分三次完成。
Step 1
实现功能:
实现基本下载,参数封装。
学习方面:
IO,URL, URLConnection, HTTPsConnection
public class DownUtils {
private final String url;
private final String fileName;
private final String targetDir;
public DownUtils (String url, String fileName, String targetDir){
this .url = url;
this .fileName = fileName;
this .targetDir = targetDir;
}
public DownUtils (String url,String fileName){
this .url = url;
this .fileName = fileName;
this .targetDir = "" ;
}
public void makeSureDir (){
File targetDir = new File(this .targetDir);
if (!targetDir.exists())
targetDir.mkdir();
}
public String getFileName (){
if (!"" .equals(targetDir)){
makeSureDir();
return targetDir + File.separator + fileName;
}
return fileName;
}
public void down (){
try (
RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw" )
)
{
URL url = new URL(this .url);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setConnectTimeout(8000 );
connection.setRequestMethod("GET" );
connection.setRequestProperty("User-Agent" , "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" );
connection.setRequestProperty("Accept" ,
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*" );
connection.setRequestProperty("Accept-Language" , "zh-CN" );
connection.setRequestProperty("Charset" , "UTF-8" );
InputStream inputStream = connection.getInputStream();
byte [] buffer = new byte [1024 ];
int hasRead = 0 ;
while ((hasRead = inputStream.read(buffer)) > 0 ){
raf.write(buffer, 0 , hasRead);
}
System.out.println("info:" + url + " download success" );
}
catch (IOException e){
e.printStackTrace();
}
}
public static void main (String[] args) {
DownUtils downUtils = new DownUtils("http://101.95.48.97:8005/res/upload/interface/apptutorials/manualstypeico/6f83ce8f-0da5-49b3-bac8-fd5fc67d2725.png"
, "yyf.png" , "C:\\Users\\donal\\Desktop\\tools" );
downUtils.down();
}
}
Step 2
实现功能:
多线程下载
学习方面:
thread
思想:按线程数将一个文件分成若干份,每个线程执行其中的一份。
package cn.Down;
/**
* Created by donal on 2017/2/21.
*/
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 实现多线程下载文件,支持断点续传
*
* 1.多线程
* 2.下载:IO流
* 3.断点续传:RandomAccessFile
* 4.下载:URL
*
* step1: 实现文件下载
* (step1,2 同时实现代码封装、整洁)
* step2: 多线程下载
* step3: 实现断点续传
*/
public class DownUtils {
private final int fileSize;
private final String url;
private final String fileName;
private final String targetDir;
private final int threadSize;
private final ExecutorService executorService;
public DownUtils (String url, String fileName, String targetDir, int threadSize){
this .url = url;
this .fileName = fileName;
this .targetDir = targetDir;
this .threadSize = threadSize;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
public DownUtils (String url,String fileName, int threadSize){
this .url = url;
this .fileName = fileName;
this .threadSize = threadSize;
this .targetDir = "" ;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
public DownUtils (String url, String fileName){
this .url = url;
this .fileName = fileName;
this .targetDir = "" ;
this .threadSize = 1 ;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
public void makeSureDir (){
File targetDir = new File(this .targetDir);
if (!targetDir.exists())
targetDir.mkdir();
}
public String getFileName (){
if (!"" .equals(targetDir)){
makeSureDir();
return targetDir + File.separator + fileName;
}
return fileName;
}
public static HttpURLConnection getConnection (String urlPath) {
try {
URL url = new URL(urlPath);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setConnectTimeout(8000 );
connection.setRequestMethod("GET" );
connection.setRequestProperty("User-Agent" , "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" );
connection.setRequestProperty("Accept" ,
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*" );
connection.setRequestProperty("Accept-Language" , "zh-CN" );
connection.setRequestProperty("Charset" , "UTF-8" );
return connection;
} catch (IOException e){
e.printStackTrace();
throw new RuntimeException(e);
}
}
public int getFileSize (){
return fileSize;
}
public void download (){
int partFileSize = fileSize / threadSize + 1 ;
int startPos = 0 ;
for (int i = 0 ; i < threadSize; ++i){
executorService.execute(new DownThread(i, startPos, partFileSize));
startPos += partFileSize;
}
executorService.shutdown();
}
private class DownThread implements Runnable {
private final int threadId;
private final int startPos;
private final int partFileSize;
public DownThread (int threadId, int startPos, int partFileSize){
this .threadId = threadId;
this .startPos = startPos;
this .partFileSize = partFileSize;
}
public void writePartFile (){
try (
InputStream inputStream = getConnection(url).getInputStream();
RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw" )
)
{
byte [] buffer = new byte [1024 ];
int hasRead;
int length = 0 ;
inputStream.skip(startPos);
raf.seek(startPos);
while ((length < partFileSize) && (hasRead = inputStream.read(buffer)) > 0 ){
raf.write(buffer, 0 , hasRead);
length += hasRead;
}
}
catch (IOException e){
e.printStackTrace();
}
}
@Override
public void run (){
System.out.println("#" + threadId + " is loading..." );
writePartFile();
System.out.println("#" + threadId + " succeeded!" );
}
}
}
package cn.Down;
/**
* Created by donal on 2017/2/23.
*/
public class DownUtilsTest {
public static void main (String[] args) {
DownUtils downUtils = new DownUtils("http://p1.gexing.com/G1/M00/C8/77/rBACJlYnjHzxEOY2AAC9B6vX98g105.jpg"
, "ais.jpg" , 5 );
downUtils.download();
}
}
Step 3
实现功能:
实现断点续传(只定义了接口)
学习方面:
File, IO。
思想:按线程名创建对应的临时文件,若要暂停或者退出,则调用setBreakPoint()函数,对临时文件写入startPos,endPos。
疑惑:如何控制线程(比如控制线程生命周期,即destroy)
结果图:
package cn.Down;
/**
* Created by donal on 2017/2/21.
*/
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DownUtils {
private final long fileSize;
private final int threadSize;
private final String url;
private final String fileName;
private final String targetDir;
private final ExecutorService executorService;
public DownUtils (String url, String fileName, String targetDir, int threadSize){
this .url = url;
this .fileName = fileName;
this .targetDir = targetDir;
this .threadSize = threadSize;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
public DownUtils (String url,String fileName, int threadSize){
this .url = url;
this .fileName = fileName;
this .threadSize = threadSize;
this .targetDir = "" ;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
public DownUtils (String url, String fileName){
this .url = url;
this .fileName = fileName;
this .targetDir = "" ;
this .threadSize = 1 ;
executorService = Executors.newFixedThreadPool(threadSize);
fileSize = getConnection(url).getContentLength();
}
private void makeSureDir (){
File targetDir = new File(this .targetDir);
if (!targetDir.exists())
targetDir.mkdir();
}
public String getFileName (){
if (!"" .equals(targetDir)){
makeSureDir();
return targetDir + File.separator + fileName;
}
return fileName;
}
private static HttpURLConnection getConnection (String urlPath) {
try {
URL url = new URL(urlPath);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
connection.setConnectTimeout(8000 );
connection.setRequestMethod("GET" );
connection.setRequestProperty("User-Agent" , "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" );
connection.setRequestProperty("Accept" ,
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, "
+ "application/x-shockwave-flash, application/xaml+xml, "
+ "application/vnd.ms-xpsdocument, application/x-ms-xbap, "
+ "application/x-ms-application, application/vnd.ms-excel, "
+ "application/vnd.ms-powerpoint, application/msword, */*" );
connection.setRequestProperty("Accept-Language" , "zh-CN" );
connection.setRequestProperty("Charset" , "UTF-8" );
return connection;
} catch (IOException e){
e.printStackTrace();
throw new RuntimeException("get connection default." );
}
}
private File fileFactory (String fileName){
return new File(fileName);
}
private boolean isFileExist (){
File targetFile = fileFactory(getFileName());
if (targetFile.exists() && targetFile.length() == fileSize){
System.out.println("The file you want to download has existed!!" );
return true ;
}
return false ;
}
private void createThread (){
long partFileSize = fileSize / threadSize + 1 ;
long startPos = 0 ;
for (int i = 0 ; i < threadSize; ++i){
executorService.execute(new DownThread(i, startPos, partFileSize));
startPos += partFileSize;
}
executorService.shutdown();
}
public void download (){
if (isFileExist())
return ;
System.out.println(url + " is ready to download.----------" );
createThread();
}
private class DownThread implements Runnable {
private long threadId;
private long startPos;
private long endPos;
private long partFileSize;
private final String tempFileName;
private transient File tempFile;
public DownThread (int threadId, long startPos, long partFileSize) {
this .threadId = threadId;
this .startPos = startPos;
this .partFileSize = partFileSize;
endPos = partFileSize + startPos;
tempFileName = "temp" + threadId + ".txt" ;
}
public void writePartFile (){
try (
InputStream inputStream = getConnection(url).getInputStream();
RandomAccessFile raf = new RandomAccessFile(getFileName(), "rw" )
)
{
byte [] buffer = new byte [1024 ];
int hasRead;
inputStream.skip(startPos);
raf.seek(startPos);
int length = 0 ;
while ((length < partFileSize) && (hasRead = inputStream.read(buffer)) > 0 ){
raf.write(buffer, 0 , hasRead);
startPos += hasRead;
}
tempFile.deleteOnExit();
}
catch (IOException e){
throw new RuntimeException("write part file default." );
}
}
private void readTempFile (){
try (
RandomAccessFile raf = new RandomAccessFile(tempFileName, "rw" )
)
{
raf.seek(0 );
startPos = raf.readLong();
raf.seek(8 );
endPos = raf.readLong();
partFileSize = endPos - startPos;
}
catch (IOException e){
throw new RuntimeException("read from temp file default." );
}
}
private void createTempFile () {
try {
tempFile = File.createTempFile(tempFileName, null );
} catch (IOException e) {
throw new RuntimeException("create new file default." );
}
}
public void makeSureTempFileExist () {
tempFile = new File(tempFileName);
if (tempFile.exists()){
readTempFile();
}else {
createTempFile();
}
}
private void setBreakPoint (){
try (
RandomAccessFile raf = new RandomAccessFile(tempFileName, "rw" )
)
{
raf.writeLong(startPos);
raf.writeLong(endPos);
}
catch (IOException e){
e.printStackTrace();
throw new RuntimeException("set break-point default." );
}
}
@Override
public void run (){
System.out.println("#" + threadId + "---Thread is loading..." );
makeSureTempFileExist();
writePartFile();
System.out.println("#" + threadId + "---Thread succeeded!" );
}
}
}
package cn.Down;
/**
* Created by donal on 2017/2/23.
*/
public class DownUtilsTest {
public static void main (String[] args) {
DownUtils downUtils = new DownUtils("https://gss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/d1a20cf431adcbef25b551dfaaaf2edda2cc9f61.jpg"
, "ais2.jpg" , 5 );
downUtils.download();
}
}