图片文件多线程断点下载

本文利用了线程池,动态链接函数,gson解析,注解,实现了多线程断点下载,并且在下载中途断网后会自动联网重新下载5次,5次连续下载失败后自动跳过该张图片,且对大部分的异常都进行了处理,本次数据来源于开源的天狗数据平台

首先解析动态函数接口的网址 http://www.tngou.net/tnfs/api/list ,默认加载20条数据,如果需要加载多条数据可以使用 http://www.tngou.net/tnfs/api/list?rows=%d 网址(%d表示你要访问几条数据),然后由该网址加载json数据,得到json对象,json对象中有图片的封面网址和size张图片集。源码是将它放在接口的注解中,反射获取后用gson解析,其中时间的转化需要适配器,json数据的格式为:

其中img为图片集的封面图片,其完整的网址为 http://tnfs.tngou.net/img (img为上图的数据),id里面有size张图片,需要下载下来,其完整的网址为http://www.tngou.net/tnfs/show/id ,id为上图的id,打开后的网页为xml数据,需要使用正则表达式解析

获取src和alt后,使用多线程下载这些数据,并并命名为alt,当然在下载过程中我们需要记录图片下载到哪里来了,省的网络异常或者自动断开后,又要重新下载,即数据加载从上次加载的地方继续加载。如果在加载中间通信失败,它会自动重新链接,如果通信失败超过5次,那么跳过该张图片,并会对该张图片进行提示。我这里在下载的时候会有进度条显示,可以看到你下载的进度【=====>>>>>>>>>>=====】,是不是很人性化啊。

该程序运行后的结果

哈哈,就是这么暴力,以后想看的话,直接运行程序,妈妈再也不要担心我要浏览网页了,,,其中.txt文件是用来记录图片下载到了哪里,省的下次又要下载,一次下载,终身免费使用。

.txt的数据格式为    封面.133390#性感气质美女短裙美腿私房照1.133390#性感气质美女短裙美腿私房照2.86636#性感气质美女短裙美腿私房照3.70731#

这里我也是使用正则表达式对数据进行处理的,当然写图片的流要用RandomAccessFile,可以从图片的任意位置写入,支持断点续传

下面为源码,没有参考任何人的代码,属于个人原创

//主函数

package com.duewang.zz;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Proiect:PhotoDownload
 * Author:Duewang
 * Time:2016/8/20 0020
 */
public class Main {
    public static void main(String[] args) {
        Assist assist = new Assist();//欢迎界面,输入你需要的数据条数
        int rows=assist.welcome();
        if(rows<=0) {
            System.out.println("请输入大于0的整数");
            System.exit(0);
        }
        HttpInter instance = Util.getInstance(HttpInter.class);//动态链接函数,gson解析,得到gson对象
        Tangou photoList = instance.getList(rows);
        if(photoList!=null){
            int i=1;
            System.out.println("本网站一共有:"+photoList.getTotal()+" 条数据,网页会随时跟新哦");
            System.out.println("本次请求图片集的封面网址:");
            for (PhotoNews photoMessage : photoList.getTangou()) {//输出你需要下载的真集封面网址
                System.out.println(i+++"."+photoMessage.getTitle()+": http://tnfs.tngou.net/img"+photoMessage.getImg());
            }
            File path=new File("res/welfare");
            Util util=new Util();
            if (util.judgeFilePath(path)) {//如果存储文件目录存在,使用线程池开启多线程
                ExecutorService pool= Executors.newFixedThreadPool(5);
                for (i=0;i<rows;i++){
//                    util.downloadThreadPoint(path,photoList,i);
                    pool.execute(new DownThread(path,i,photoList));
                }
                pool.shutdown();
            }
        }

    }
}
//欢迎界面

package com.duewang.zz;

import java.util.Scanner;

/**
 * Proiect:PhotoDownload
 * Author:Duewang
 * Time:2016/8/20 0020
 */
public class Assist {
    public int welcome(){
        System.out.println("欢迎使用duewang图片下载管理");
        Scanner input=new Scanner(System.in);
        System.out.print("你要下载多少条数据:");
        int rows=input.nextInt();
        input.close();
        return rows;
    }
}
//gson数据对象
package com.duewang.zz;

import com.google.gson.annotations.SerializedName;

import java.util.List;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/19 0019
 */
public class Tangou {
    @SerializedName("status")
    private boolean status;
    @SerializedName("total")
    private int total;
    @SerializedName("tngou")
    private List<PhotoNews> tangou;

    public boolean isStatus() {
        return status;
    }

    public void setStatus(boolean status) {
        this.status = status;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }

    public List<PhotoNews> getTangou() {
        return tangou;
    }

    public void setTangou(List<PhotoNews> tangou) {
        this.tangou = tangou;
    }
}
//gson数据对象
package com.duewang.zz;

import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;

import java.util.Calendar;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/19 0019
 */
public class PhotoNews {
    @SerializedName("id")
    private long id;
    @SerializedName("title")
    private String title;
    @SerializedName("gallertclass")
    private int gallertclass;
    @SerializedName("count")
    private int count;
    @SerializedName("size")
    private int size;
    @SerializedName("img")
    private String img;
    @JsonAdapter(CalendarType.class)
    private Calendar time;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getGallertclass() {
        return gallertclass;
    }

    public void setGallertclass(int gallertclass) {
        this.gallertclass = gallertclass;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getImg() {
        return img;
    }

    public void setImg(String img) {
        this.img = img;
    }

    public Calendar getTime() {
        return time;
    }

    public void setTime(Calendar time) {
        this.time = time;
    }
}
//时间类型转化适配器
package com.duewang.zz;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.Calendar;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/19 0019
 */
public class CalendarType extends TypeAdapter<Calendar> {

    @Override
    public void write(JsonWriter jsonWriter, Calendar calendar) throws IOException {
        if (calendar!=null){
            long timeInMillis = calendar.getTimeInMillis();
            jsonWriter.value(timeInMillis);
        }else {
            jsonWriter.nullValue();
        }
    }

    @Override
    public Calendar read(JsonReader jsonReader) throws IOException {
        if(jsonReader.hasNext()){
            long l = jsonReader.nextLong();
            Calendar instance = Calendar.getInstance();
            instance.setTimeInMillis(l);
            return instance;
        }
        return null;
    }
}
//动态接口函数
package com.duewang.zz;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/19 0019
 */
public interface HttpInter {
    @GET("http://www.tngou.net/tnfs/api/list?rows=%d")
    Tangou getList(int rows);
}
//注解
package com.duewang.zz;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/19 0019
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
    String value();
}
//动态接口实现,多线程的主方法,记录与判断图片的下载,网络断开后可以自动重连5次,接着上次下载的地方继续下载
package com.duewang.zz;

import com.google.gson.Gson;

import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Proiect:PhotoDownload
 * Author:Duewang
 * Time:2016/8/20 0020
 */
public class Util {

    public static<T> T getInstance(Class<T> t){
        Object o = Proxy.newProxyInstance(t.getClassLoader(), new Class[]{t}, new MyHandler());
        return (T) o;
    }

    private static class MyHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args){
            GET get = method.getAnnotation(GET.class);
            String url=String.format(get.value(),args);
            Class<?> returnType = method.getReturnType();
            boolean flag;
            int count=0;
            do{
                if(count==5){//控制网络的访问次数
                    System.out.println("网络或者数据异常,我也无能为力");
                    break;
                }
                flag=false;
                count++;
                List list=linkWeb(url,0);
                if(list.get(0).equals("true")){
                    int contentLength= (int) list.get(1);
                    InputStream is= (InputStream) list.get(2);
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    int length;
                    int sum=0;
                    byte[] buffer=new byte[102400];
                    try {
                        while ((length = is.read(buffer)) != -1) {
                            bos.write(buffer,0,length);
                            if(contentLength>1){
                                sum+=length;
                                System.out.print("\rjson数据获取中");
                                print(contentLength,sum);
                            }
                        }
                        is.close();
                        Gson gson=new Gson();
                        System.out.println("\n开始解析json数据");
                        return gson.fromJson(bos.toString("UTF-8"),returnType);
                    } catch (IOException e) {
                        System.out.println("网络数据转换失败,正在重新链接获取新数据");
                        flag=true;//当链接断开后,在次请求链接
                    }
                }else{
                    System.out.println("网络异常,正在重新链接");
                    flag=true;//当网络访问失败后,在次请求链接
                }
            }while(flag);
            return null;
        }
    }

    public static void print(int contentLength, int sum){
        double percent;
        int p;
        int i;
        System.out.print("[");
        percent= 1.0*sum/contentLength;
        p= (int) (percent*20);
        for(i=0;i<20;i++){
            if(i<p){
                System.out.print("=");
            }else if(i==p){
                System.out.print(">");
            }else{
                System.out.print(" ");
            }
        }
        System.out.print("]");
    }

    //返回网络链接的有效数据
    public static List linkWeb(String url,int start){
        HttpURLConnection connection;
        List list=new ArrayList();
        try {
            connection = (HttpURLConnection) new URL(url).openConnection();
            Map<String, List<String>> headerFields = connection.getHeaderFields();
            List<String> range=headerFields.get("Accept-Ranges");
            connection.disconnect();
            connection = (HttpURLConnection) new URL(url).openConnection();
            if(range!=null){//是否支持网络断点下载
                connection.setRequestProperty("Range","bytes="+start+"-");
            }
            connection.setRequestMethod("GET");
            connection.setDoInput(true);
            connection.setReadTimeout(70000);
            connection.setConnectTimeout(20000);
            int responseCode=connection.getResponseCode();
            int contentLength = connection.getContentLength();
            if(responseCode==200||responseCode==206)
            {
                InputStream is = connection.getInputStream();
                list.add("true");
                list.add(contentLength);
                list.add(is);
                if(range!=null){
                    list.add("true");
                }else{
                    list.add("false");
                }
            } else{
                list.add("false");
            }
        } catch (IOException e) {
            list.add("error");
        }
        return list;
    }

    public boolean judgeFilePath(File file){
        if (file.exists()) {
            if (file.isDirectory()) {
                return true;
            }else{
                System.out.println("你的输入的不是一个存储目录");
            }
        }else{
            System.out.println("你输入的存储目录不存在");
        }
        return false;
    }

    public void createFilePath(File file){
        if (!file.exists()) {
            file.mkdir();
        }
    }

    public boolean createFile(File file){
        if(!file.exists()){
            try {
                file.createNewFile();
                return true;
            } catch (IOException e) {
                System.out.println("无法创建文件:"+file.toString());
                return false;
            }
        }
        return true;
    }

    public boolean create(File path,String fileName){
        path = new File(path.toString() + "/" + fileName);
        createFilePath(path);
        File file=new File(path.toString()+"/"+fileName+".txt");
        if(createFile(file)){
            return true;
        }
        return false;
    }

    public void downloadThreadPoint(File path,Tangou photoList,int index){
        String fileName = photoList.getTangou().get(index).getTitle();
        if(create(path,fileName)){
            String url="http://tnfs.tngou.net/img"+photoList.getTangou().get(index).getImg(),str;
            long id=photoList.getTangou().get(0).getId();
            try {
                FileInputStream fis=new FileInputStream(path.toString()+"/"+fileName+"/"+fileName+".txt");
                byte[] bytes=new byte[102400];
                int length;
                int start;
                length=fis.read(bytes);
                if(length>1) {
                    str=new String(bytes,0,length,"UTF-8");
                }else{
                    str="";
                }
                fis.close();
                Pattern compile = Pattern.compile("封面[.](.+?)#");
                Matcher matcher = compile.matcher(str);
                if(matcher.find()){
                    start=Integer.parseInt(matcher.group(1));
                }else{
                    start=0;
                }
                int count=0;
                boolean flag;
                int sum=0;
                do{
                    if(count==5){
                        System.out.println("网络或者数据异常,封面图片加载我也无能为力");
                        break;
                    }
                    flag=false;
                    count++;
                    RandomAccessFile raf=new RandomAccessFile(new File(path.toString()+"/"+fileName+"/"+"封面"+".jpg"),"rw");
                    List list=linkWeb(url,start);
                    if(list.get(0).equals("true")){
                        int contentLength= (int) list.get(1);
                        InputStream is= (InputStream) list.get(2);
                        if(list.get(3).equals("true")){
                            raf.seek(start);
                            sum=start;
                        }
                        if(contentLength>1){
                            contentLength+=sum;
                        }
                        try {
                            while ((length = is.read(bytes)) != -1) {
                                raf.write(bytes,0,length);
                                sum+=length;
                                if(start==0){
                                    str=str+"封面."+sum+"#";
                                    start=sum;
                                }else{
                                    str=str.replaceAll("封面[.](.+?)#","封面."+sum+"#");
                                }
                                FileOutputStream fos=new FileOutputStream(path.toString()+"/"+fileName+"/"+fileName+".txt");
                                fos.write(str.getBytes("UTF-8"));
                                fos.close();
                                if(contentLength>1){
                                    System.out.print("\r图片封面加载中");
                                    print(contentLength,sum);
                                }
                          }
                             raf.close();
                            if(contentLength>1){
                                System.out.println("\n第"+(index+1)+"条数据的封面图片加载完毕");
                            }else{
                                System.out.println("第"+(index+1)+"条数据的封面图片加载完毕");
                            }
                    } catch (IOException e) {
                        System.out.println("网络数据转换失败,正在重新链接获取新数据");
                        flag=true;//当链接断开后,在次请求链接
                    }
                    }else if(list.get(0).equals("false")){
                        System.out.println("第"+(index+1)+"条数据的封面图片已经下载完毕,无需下载");
                        break;
                    }else{
                        System.out.println("网络异常,正在重新链接");
                        flag=true;
                    }
                }while(flag);
            } catch (FileNotFoundException e) {
                System.out.println(path.toString()+"/"+fileName+"/"+fileName+".txt文件打开失败");
            } catch (IOException e) {
                System.out.println(path.toString()+"/"+fileName+"/"+fileName+"文件读取异常");
            }
            url="http://www.tngou.net/tnfs/show/"+id;
            int count=0;
            boolean flag;
            do{
                flag=false;
                count++;
                List list=linkWeb(url,0);
                if(list.get(0).equals("true")){
                    int contentLength= (int) list.get(1);
                    InputStream is= (InputStream) list.get(2);
                    int sum=0;
                    int length;
                    byte[] buffer=new byte[102400];
                    ByteArrayOutputStream bos=new ByteArrayOutputStream();
                    try {
                        if(contentLength<2){
                            System.out.println("真集网址加载中[=====>>>>>>>>>>=====]");
                        }
                        while ((length = is.read(buffer)) != -1) {
                            bos.write(buffer,0,length);
                            if(contentLength>1){
                                sum+=length;
                                System.out.print("\r真集网址加载中");
                                print(contentLength,sum);
                            }
                        }
                        str=bos.toString("UTF-8");
                        is.close();
                        bos.close();
                        Pattern compile = Pattern.compile("<img  class=\"(.+?)\" src=\"(.+?)\" alt=\"(.+?)\">");
                        Matcher matcher = compile.matcher(str);
                        if(contentLength>1){
                            System.out.println("\n真集网址已全部解析,正在全力加载真集图片");
                        }else{
                            System.out.println("真集网址已全部解析,正在全力加载真集图片");
                        }
                        while (matcher.find()) {
                            url=matcher.group(2);
                            String fileNameTemp=matcher.group(3);
                            FileInputStream fis=new FileInputStream(path.toString()+"/"+fileName+"/"+fileName+".txt");
                            int start;
                            length=fis.read(buffer);
                            if(length>1) {
                                str=new String(buffer,0,length,"UTF-8");
                            }else{
                                str="";
                            }
                            fis.close();
                            Pattern compileTemp = Pattern.compile(fileNameTemp+"[.](.+?)#");
                            Matcher matcherTemp = compileTemp.matcher(str);
                            if(matcherTemp.find()){
                                start=Integer.parseInt(matcherTemp.group(1));
                            }else{
                                start=0;
                            }
                            count=0;
                            do{
                                if(count==5){
                                    System.out.println("\n网络或者数据异常,真集的加载我也无能为力,已为你跳过该张真集:"+fileNameTemp);
                                    break;
                                }
                                flag=false;
                                count++;
     RandomAccessFile raf=new RandomAccessFile(new File(path.toString()+"/"+fileName+"/"+fileNameTemp+".jpg"),"rw");
                                list=linkWeb(url,start);
                                if(list.get(0).equals("true")){
                                    contentLength= (int) list.get(1);
                                    is= (InputStream) list.get(2);
                                    if(list.get(3).equals("true")){
                                        raf.seek(start);
                                        sum=start;
                                    }
                                    if(contentLength>1){
                                        contentLength+=sum;
                                    }
                                    try {
                                        while ((length = is.read(buffer)) != -1) {
                                            raf.write(buffer,0,length);
                                            sum+=length;
                                            if(start==0){
                                                str=str+fileNameTemp+"."+sum+"#";
                                                start=sum;
                                            }else{
                                                str=str.replaceAll(fileNameTemp+"[.](.+?)#",fileNameTemp+"."+sum+"#");
                                            }
                 FileOutputStream fos=new FileOutputStream(path.toString()+"/"+fileName+"/"+fileName+".txt");
                                            fos.write(str.getBytes("UTF-8"));
                                            fos.close();
                                            if(contentLength>1){
                                                System.out.print("\r“"+fileNameTemp+"”正在加载中");
                                                print(contentLength,sum);
                                            }
                                        }
                                        raf.close();
                                        if(contentLength>1){
                                            System.out.println("");
                                        }
                                    } catch (IOException e) {
                                        System.out.println("网络数据转换失败,正在重新链接获取新数据:"+fileNameTemp);
                                        flag=true;
                                    }
                                }else if(list.get(0).equals("false")){
                                    System.out.println("该张真集图片已经下载完毕,无需下载:"+fileNameTemp);
                                    break;
                                }else{
                                    System.out.println("网络异常,正在重新链接");
                                    flag=true;
                                }
                            }while(flag);
                        }
                        System.out.println("\n本条数据已经全部下载完成:"+fileName);
                    } catch (IOException e) {
                        System.out.println("网络数据读取失败,正在重新链接获取新数据");
                        flag=true;
                    }
                }else{
                    System.out.println("网络异常,正在重新链接真集网址");
                }
                if(count==4){
                    System.out.println("网络或者数据异常,真集网址加载我也无能为力");
                    break;
                }
            }while(flag);
        }
    }

}
//多线程实现Runnable接口
package com.duewang.zz;

import java.io.File;

/**
 * Proiect:Day18
 * Author:Duewang
 * Time:2016/8/20 0020
 */
public class DownThread implements Runnable {
    private File path;
    private int index;
    private Tangou list;

    public DownThread(File path, int index, Tangou list) {
        this.path = path;
        this.index = index;
        this.list = list;
    }

    @Override
    public void run() {
        Util util=new Util();
        util.downloadThreadPoint(path,list,index);
    }
}

至此,程序完结,源码有效,个人亲测







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值