在對網絡文件下載進行讀寫操作時,有時候會因爲網絡問題導致 “Connection reset” 異常以及有時會在一半的時候卡住,針對這個兩種情況需要重新去下載,第一種異常比較好判斷,可以直接拿錯誤信息即可判斷,第二種情況想到的辦法就是利用線程去解決,給下載單獨開啓一個子線程,並且給該子線程設定一個超時時間,當超過改時間時,則取消下載,中斷該子線程!
在這裏通過實現Callable接口類來設置超時任務:
package com.java.mytest;
import com.csvreader.CsvReader;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import java.io.*;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
/**
* @Author win7
* @Date 12/1/16 2:45 PM
*/
public class TestDownloadFile {
private static String tempUrl = "http://xxxx/test.txt";
private static String filePath;
public static void main(String[] args) {
String suffixName = tempUrl.substring(tempUrl.lastIndexOf("/") + 1, tempUrl.length());
int timeout = 20; //秒.
int num = 1;
Boolean flag = null;
while (true) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Boolean result = false;
DownloadFile downloadFile = new DownloadFile(suffixName, num);
Future<Boolean> future = executor.submit(downloadFile);// 將任務提交到線程池中
try {
future.get(timeout, TimeUnit.SECONDS);// 設定在20秒的時間內完成
} catch (InterruptedException e) {
filePath = null;
System.out.println("線程中斷出錯--------"+num);
} catch (ExecutionException e) {
filePath = null;
System.out.println("線程服務出錯--------"+num);
} catch (TimeoutException e) {// 超時異常
filePath = "Connection reset";
System.out.println("線程服務超時--------"+num);
} finally {
//關閉此下載任務
downloadFile.setIsStop(true);
flag = future.cancel(true);// 中斷執行此任務的線程
System.out.println("線程服務關閉--------"+num);
System.out.println("result is " + result);
System.out.println("刪除結果:" + flag);
executor.shutdownNow();//關閉ExecutorService,阻止等待任務啓動並試圖停止當前正在執行的任務
//超過3次終止整個任務
if (num >= 3){
break;
}
}
if (StringUtils.isBlank(filePath)){
System.out.println("download file is error!");
break;
}else {
if ("Connection reset".equalsIgnoreCase(filePath)){
System.out.println("============Connection reset continue==========="+num);
num++;
continue;
}else {
File file = new File(filePath);
if (file.exists()){
System.out.println("============SUCCESS==========="+num);
break;
}else {
System.out.println("============"+filePath+" continue==========="+num);
continue;
}
}
}
}
}
static class DownloadFile implements Callable<Boolean> {
private String fileName;
private int num;
//是否關閉此下載任務
private boolean isStop = false;
public DownloadFile(String suffixName, int num) {
this.fileName = suffixName;
this.num = num;
}
/**
* 將文件下載到本地
*/
@Override
public Boolean call(){
filePath = "F:/" + fileName;
URL url = null;
URLConnection conn = null;
try {
url = new URL(tempUrl);
conn = url.openConnection();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (null == url || null == conn){
filePath = null;
return false;
}
try (
//try語句結束後自動關閉資源
InputStream inputStream = conn.getInputStream();
FileOutputStream outputStream = new FileOutputStream(filePath)
) {
byte[] buffer = new byte[1204];
int length;
while ((length = inputStream.read(buffer)) != -1) {
if (isStop){
break;
}
outputStream.write(buffer, 0, length);
}
}catch (Exception e){
filePath = e.getMessage();
e.printStackTrace();
}finally {
File file = new File(filePath);
if (file.exists()){
return true;
}
return false;
}
}
public void setIsStop(boolean isStop) {
this.isStop = isStop;
}
}
}
記錄下上面代碼遇到的坑:
如果任務超時了,從而執行了future.cancel(true)以及executor.shutdownNow()此時這個線程就應該終結,但是實際上由於用到了IO讀寫操作,並且是阻塞式的,所以這個線程並沒有終止而是繼續的存在的,要知道jvm只有在所有(非守護)線程推出後纔會退出,所以此時的jvm並沒有因爲方法執行完畢而退出。
解決辦法:
1、是在讀寫的時候加上一個是否停止的標識
2、將阻塞式的IO讀寫操作換成不阻塞的NIO讀寫操作(此方法沒去測試,待下次有時間在測)