利用Http在Android客户端与Web服务器之间断点续传中大文件


一、还是先说一下需要用到的工具:

1.服务端:IDEA(真的超好用)、Tomcat

2.客户端:Android


二、上代码

服务端代码:

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

@WebServlet(name = "downloadServlet")
public class downloadServlet extends HttpServlet {


   // String sfilepath="G:/niki/upload/百年孤独.txt";


    String sfilepath="G:/niki/upload/pics.rar";
    String filename;
    String bytes_downloaded;
    private Socket socket;


    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        System.out.println("客户端来啦!!!!!!!!");            //请各位读者忽略我这个调试的习惯用语……
        byte[] buffer=new byte[1024];
        InputStream inputStream=request.getInputStream();
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        int length;
        if ((length=inputStream.read(buffer))!=-1){
            byteArrayOutputStream.write(buffer,0,length);
        }
        String str=String.valueOf(byteArrayOutputStream);
        if(str.length()>3){
            String str_result=URLDecoder.decode(str.substring(1,str.length()-1),"UTF-8");

            String str_response="准备发送文件的服务器已经收到客户端的消息:\n";
            System.out.println(str_response+str_result);
            JSONObject jsonObject= JSONObject.fromObject(str_result);
            filename=jsonObject.getString("filename");
            bytes_downloaded=jsonObject.getString("bytes_downloaded");
            System.out.println("从客户端收到的消息为:\n文件名:《"+filename+"》\n已经下载的字节数为:"+bytes_downloaded);

        }
        String path="G:/niki/upload/";//构建上传路径
        response.setCharacterEncoding("utf-8");

        OutputStreamWriter osw = null;
        osw = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
        PrintWriter pw = new PrintWriter(osw, true);



        Map<String ,String> map=new HashMap<>();
        map.put("filename", URLEncoder.encode(filename,"UTF-8"));
        System.out.println("客户端说已经下载了:"+bytes_downloaded+"个字节");
        File file=new File(sfilepath);
        if(file.isFile()&&file.exists()){
            System.out.println("此书存在");

            response.addHeader("bytes_counts", String.valueOf(file.length()));
            response.addHeader("filename","pics.rar");
            DataOutputStream dataOutputStream=new DataOutputStream(response.getOutputStream());
            RandomAccessFile randomAccessFile=new RandomAccessFile(sfilepath,"r");
            byte[]  filebuffer=new byte[1024];
            randomAccessFile.skipBytes(Integer.parseInt(bytes_downloaded));
            int len= randomAccessFile.read(filebuffer);
            if (len!=-1){
                System.out.println("刚刚发送了"+len+"个字节");
                dataOutputStream.write(filebuffer,0,len);
                dataOutputStream.flush();
            }

        }else{

        }


        pw.flush();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

客户端:

1.DownLoadFileActivity

package com.example.administrator.filetransportapp.FileOperator;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.example.administrator.filetransportapp.R;
import com.example.administrator.filetransportapp.Utils.Utils;

import net.sf.json.JSONArray;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * Created by Administrator on 2017/11/8.
 */

public class DownLoadFileActivity extends Activity {

    /**
     * 控件
     * */
    Button btdownload;
    ProgressBar mpBar;
    TextView tvPbar;                                //用来显示下载的进度
    TextView tvserverdata;                          //用来显示服务端返回的数据

    /***
     * 通信所需
     * */
    Handler handler=new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //获取数据
            final String mess=msg.getData().getString("message").toString();
            final int bytes_sum=msg.getData().getInt("bytes_sum");
            final int bytes_downloaded=msg.getData().getInt("bytes_downloaded");

            //改变UI的显示
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvserverdata.setText("下载中...");
                    tvPbar.setText(bytes_downloaded+"/"+bytes_sum);
                    mpBar.setMax(bytes_sum);
                    mpBar.setProgress(bytes_downloaded);
                }
            });
           if(bytes_downloaded<bytes_sum){


                Runnable task=new Runnable() {
                    @Override
                    public void run() {
                        task_count++;

                        downFileThread=new HttpDownLoadThread(handler,filename,DownLoadFileActivity.this,false);
                        downFileThread.start();
                    }
                };
                excutor.execute(task);
            }else{

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                        tvserverdata.setText("下载完成!");
                    }
                });
            }
            return true;
        }
    });

    boolean flag=true;
    HttpDownLoadThread downFileThread;
    String filename="pics.rar";

    //String filename="百年孤独.txt";




    Executor excutor;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.downloadfileactivity);
        Init();
        excutor= Executors.newFixedThreadPool(40);      //初始化一个固定数量为40的线程池
    }

    //测试多线程
    public void TestExcution(){



    }


    public void Init(){

        btdownload= (Button) findViewById(R.id.bt_download);
        mpBar=(ProgressBar)findViewById(R.id.progressbar);
        tvPbar=(TextView)findViewById(R.id.tv_progress);
        tvserverdata=(TextView)findViewById(R.id.server_data);

    }

    int task_count=0;
    public void DownLoadFile(View v){
        
            new Thread(new Runnable() {
                @Override
                public void run() {

                    downFileThread=new HttpDownLoadThread(handler,filename,DownLoadFileActivity.this,true);
                    downFileThread.run();
                }
            }).start();
    }



}

2.HttpDownLoadThread

package com.example.administrator.filetransportapp.FileOperator;

import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;

import com.example.administrator.filetransportapp.Utils.Utils;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.http.protocol.ResponseDate;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2017/11/10.
 */

public class HttpDownLoadThread extends Thread {
    Context context;

    String filename="";

    int bytes_sum=0;                            //此次任务需要下载的总量
    int bytes_downloaded=0;                     //此次任务已经下载的字节数
    int MAX=1024;                               //此次任务每次最多接收的数据量大小
    byte[] buffer=new byte[MAX];


    boolean flag=false;                         //标志此次任务是否成功打开通信链路

    StringBuffer stringBuffer=new StringBuffer();
    /***
     * 用于此线程与主线程进行通信
     * */
    Handler handler;
    Bundle b;

    //文件路径+文件名  [此处最好是把文件格式给带上]
    int count=0;
    File dir;

    String externalstorsgepath="";
    public HttpDownLoadThread(Handler handler,String Filename,Context context,boolean flag){
        this.handler=handler;
        this.filename=Filename;
        this.context=context;


        externalstorsgepath= getSdCardPath();
        dir=new File(externalstorsgepath+"/nikidata");

        if(dir.exists()){
            //dir.mkdirs();
            System.out.println("目录已经存在");

            File file=new File(dir.getAbsolutePath(),filename);
            if(file.exists()){
                if(flag){

                    file.delete();
                    System.out.println("已经删除了"+file.getAbsolutePath());
                    try {
                        file.createNewFile();
                        System.out.println("以及成功创建"+file.getAbsolutePath());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }else{

                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.out.println("以及成功创建"+file.getAbsolutePath());
            }

        }


    }

    @Override
    public void run() {

        //构建URL的格式为:    http://IP地址:监听的端口号/Servlet的路径
        final String strUrl = "http://"+ Utils.IP+":8080/downloadServlet";
        final URL[] url = {null};

        //第一步:访问网站,进行连接
        try {
            url[0] =new URL(strUrl);
            HttpURLConnection urlConn=(HttpURLConnection) url[0].openConnection();
            urlConn.setDoInput(true);       //setting inputstream using bytestream
            urlConn.setDoOutput(true);

            urlConn.setRequestMethod("POST");

            urlConn.setUseCaches(false);
            urlConn.setRequestProperty("Content-Type","application/x-ww-form-urlencoded");  //
            urlConn.setRequestProperty("Charset","utf-8");
            urlConn.connect();


            bytes_downloaded=GetDownloadedBytes();
            System.out.println("准备好发送的数据");
            //第二步:准备好发送的数据
            Map<String ,String> map=new HashMap<>();
            map.put("filename",URLEncoder.encode(filename,"UTF-8"));
            map.put("bytes_downloaded", String.valueOf(bytes_downloaded));
            JSONArray jsonarray= JSONArray.fromObject(map);

            //第三步:打开数据通道
            DataOutputStream dop=new DataOutputStream(urlConn.getOutputStream());
            dop.write(String.valueOf(jsonarray).getBytes());

            //第四步:将准备的数据发送给服务器
            dop.flush();
            dop.close();

            long h=urlConn.getHeaderFieldDate("bytes_counts",555);
            bytes_sum= Integer.parseInt(urlConn.getHeaderField("bytes_counts"));

            InputStream inputStream=urlConn.getInputStream();
            File dir=new File(externalstorsgepath+"/nikidata");
            File file=new File(dir.getAbsolutePath(),filename);
            String fileexists =dir.getAbsolutePath()+"/"+filename;
            if (file.exists()) {

                System.out.println("进入续传");
                long filelength = file.length();
                RandomAccessFile fileff = new RandomAccessFile(fileexists,"rw");
                System.out.println("文件存放位置"+fileexists);
                System.out.println("续传长度标记"+filelength);


                byte[] by = new byte[1024];
                int start =(int)filelength;
                int amount;
                System.out.println("从输入流中读数据");
                fileff.seek(filelength);
                //fileff.skipBytes(start);
                System.out.println("当前指针位置"+fileff.getFilePointer());
                while ((amount = inputStream.read(by)) != -1) {
                    fileff.write(by,0,amount);
                }
                System.out.println("结束");
                fileff.close();

            } else {

                System.out.println("文件不存在直接传送");

            }
            NotifyMainThread();         //通知主线程,下载进度已经更新

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }catch (ProtocolException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }




    }


    @Override
    public synchronized void start() {
        super.start();
    }

    public int GetDownloadedBytes(){

        File dir=new File(externalstorsgepath+"/nikidata");
        File file=new File(dir.getAbsolutePath(),filename);
        // File file=new File(dir.getAbsolutePath(),"pics.rar");
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


        return (int) file.length();

    }



    /**
     * 判断SDCard是否存在 [当没有外挂SD卡时,内置ROM也被识别为存在sd卡]
     *
     * @return
     */
    public static boolean isSdCardExist() {
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

    /**
     * 获取SD卡根目录路径
     *
     * @return
     */
    public static String getSdCardPath() {
        boolean exist = isSdCardExist();
        String sdpath = "";
        if (exist) {
            sdpath = String.valueOf(Environment.getExternalStorageDirectory());
        } else {
            sdpath = "不适用";
        }
        return sdpath;

    }

    public void NotifyMainThread(){


        if(b==null){

            b=new Bundle();
            b.putString("message","hello I am a thread");
            b.putInt("bytes_sum",bytes_sum);
            b.putInt("bytes_downloaded",bytes_downloaded);
            Message message=new Message();
            message.setData(b);
            handler.sendMessage(message) ;
        }

    }
}

效果图就先不上了,因为现在已经六点半了,我赶着去吃饭。

做个总结吧:

1.我原本是想用Socket做长连接的,但是我师父说保持长连接的研发成本比Http要高,因为我用Socket时,确实也遇见了很难搞定的异常:Java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)。如果有人成功解决过这个异常,希望可以不吝赐教可怜

2.改用Http之后,我认为服务端应该至少传一个参数给客户端,告诉要下载的文件的总字节数,我刚开始是想用JSON,这绝对是序列化的神器呀,对吧?可是我在客户端反序列化的时候,却遇见好几个极难解决的问题,逼得我……都手动写代码去反序列化了。 后来发现,其实我可以直接在response的头部加一个参数再见,折腾了我那么久!

3.写程序还是要注意保护眼睛,睡个午觉。



谢谢大家的阅读,祝生活愉快~吐舌头


猜你喜欢

转载自blog.csdn.net/qq_37385181/article/details/78502303