一、还是先说一下需要用到的工具:
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.写程序还是要注意保护眼睛,睡个午觉。
谢谢大家的阅读,祝生活愉快~