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

来源:互联网 发布:python ctrl c 编辑:程序博客网 时间:2024/06/10 15:28


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

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.写程序还是要注意保护眼睛,睡个午觉。



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


阅读全文
0 0