在对网络文件下载进行读写操作时,有时候会因为网络问题导致 “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读写操作(此方法没去测试,待下次有时间在测)