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