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