断点续传和下载原理分析
最近做一个文件上传和下载的应用对文件上传和下载进行了一个完整的流程分析以来是方便自己对文件上传和下载的理解,而来便于团队内部的分享
故而做了几张图,将整体的流程都画下来,便于大家的理解和分析,如果有不完善的地方希望
大家多提意见,
由于参考了网上许多的资料,特此感谢
首先是文件上传,这个要用到服务器
http://dl.iteye.com/upload/attachment/601591/b6ae38e3-cdf9-3739-be47-a78ad0b24b81.jpg
关键代码:
FileServer.java
import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.PushbackInputStream;import java.io.RandomAccessFile;import java.net.ServerSocket;import java.net.Socket;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Map;import java.util.Properties;import java.util.Set;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import util.FileLogInfo;import util.StreamTool;public class FileServer { private ExecutorService executorService;//线程池 private int port;//监听端口 private boolean quit = false;//退出 private ServerSocket server; private Map<Long, FileLogInfo> datas = new HashMap<Long, FileLogInfo>();//存放断点数据,以后改为数据库存放 public FileServer(int port) { this.port = port; //创建线程池,池中具有(cpu个数*50)条线程 executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50); } /*** 退出*/ public void quit() {this.quit = true;try {server.close();}catch (IOException e) {e.printStackTrace();} }/*** 启动服务* @throws Exception*/ public void start() throws Exception { server = new ServerSocket(port);//实现端口监听 while(!quit) { try { Socket socket = server.accept(); executorService.execute(new SocketTask(socket));//为支持多用户并发访问,采用线程池管理每一个用户的连接请求 }catch (Exception e) { e.printStackTrace(); } } }private final class SocketTask implements Runnable {private Socket socket = null;public SocketTask(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {System.out.println("FileServer accepted connection "+ socket.getInetAddress()+ ":"+ socket.getPort());//得到客户端发来的第一行协议数据:Content-Length=143253434;filename=xxx.3gp;sourceid=//如果用户初次上传文件,sourceid的值为空。InputStream inStream = socket.getInputStream();String head = StreamTool.readLine(inStream);System.out.println("FileServer head:"+head);if(head!=null){//下面从协议数据中提取各项参数值String[] items = head.split(";");String filelength = items.substring(items.indexOf("=")+1);String filename = items.substring(items.indexOf("=")+1);String sourceid = items.substring(items.indexOf("=")+1);//生成资源id,如果需要唯一性,可以采用UUIDlong id = System.currentTimeMillis();FileLogInfo log = null;if(sourceid!=null && !"".equals(sourceid)){id = Long.valueOf(sourceid);//查找上传的文件是否存在上传记录log = find(id);}File file = null;int position = 0;//如果上传的文件不存在上传记录,为文件添加跟踪记录if(log==null){//设置存放的位置与当前应用的位置有关File dir = new File("c:/temp/");if(!dir.exists()) dir.mkdirs();file = new File(dir, filename);//如果上传的文件发生重名,然后进行改名if(file.exists()){filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+ filename.substring(filename.indexOf("."));file = new File(dir, filename);}save(id, file);}// 如果上传的文件存在上传记录,读取上次的断点位置else{System.out.println("FileServer have exits log not null");//从上传记录中得到文件的路径file = new File(log.getPath());if(file.exists()){File logFile = new File(file.getParentFile(), file.getName()+".log");if(logFile.exists()){Properties properties = new Properties();properties.load(new FileInputStream(logFile));//读取断点位置position = Integer.valueOf(properties.getProperty("length"));}}}//***************************上面是对协议头的处理,下面正式接收数据***************************************//向客户端请求传输数据OutputStream outStream = socket.getOutputStream();String response = "sourceid="+ id+ ";position="+ position+ "%";//服务器收到客户端的请求信息后,给客户端返回响应信息:sourceid=1274773833264;position=position//sourceid由服务生成,唯一标识上传的文件,position指示客户端从文件的什么位置开始上传outStream.write(response.getBytes());RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");//设置文件长度if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//移动文件指定的位置开始写入数据fileOutStream.seek(position);byte[] buffer = new byte;int len = -1;int length = position;//从输入流中读取数据写入到文件中,并将已经传入的文件长度写入配置文件,实时记录文件的最后保存位置while( (len=inStream.read(buffer)) != -1){fileOutStream.write(buffer, 0, len);length += len;Properties properties = new Properties();properties.put("length", String.valueOf(length));FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName()+".log"));//实时记录文件的最后保存位置properties.store(logFile, null);logFile.close();}//如果长传长度等于实际长度则表示长传成功if(length==fileOutStream.length()){delete(id);}fileOutStream.close();inStream.close();outStream.close();file = null;}}catch (Exception e) {e.printStackTrace();}finally{ try { if(socket!=null && !socket.isClosed()) socket.close(); } catch (IOException e) { e.printStackTrace(); } }} }/*** 查找在记录中是否有sourceid的文件* @param sourceid* @return*/ public FileLogInfo find(Long sourceid) { return datas.get(sourceid); }/*** 保存上传记录,日后可以改成通过数据库存放* @param id* @param saveFile*/ public void save(Long id, File saveFile) { System.out.println("save logfile "+id); datas.put(id, new FileLogInfo(id, saveFile.getAbsolutePath())); }/*** 当文件上传完毕,删除记录* @param sourceid*/ public void delete(long sourceid) { System.out.println("delete logfile "+sourceid); if(datas.containsKey(sourceid)) datas.remove(sourceid); } } 由于在上面的流程图中已经进行了详细的分析,我在这儿就不讲了,只是在存储数据的时候服务器没有用数据库去存储,这儿只是为了方便,所以要想测试断点上传,服务器是不能停的,否则数据就没有了,在以后改进的时候应该用数据库去存储数据。
文件上传客户端:
http://dl.iteye.com/upload/attachment/601593/93822739-7657-3360-9d6f-08de67dbc000.jpg
关键代码:
UploadActivity.java
package com.hao;import java.io.File;import java.util.List;import com.hao.upload.UploadThread;import com.hao.upload.UploadThread.UploadProgressListener;import com.hao.util.ConstantValues;import com.hao.util.FileBrowserActivity;import android.app.Activity;import android.app.Dialog;import android.app.ProgressDialog;import android.content.DialogInterface;import android.content.Intent;import android.content.res.Resources;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;/** ** @author Administrator * */public class UploadActivity extends Activity implements OnClickListener{private static final String TAG = "SiteFileFetchActivity";private Button download, upload, select_file;private TextView info;private static final int PROGRESS_DIALOG = 0;private ProgressDialog progressDialog;private UploadThread uploadThread;private String uploadFilePath = null;private String fileName; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.upload); initView(); } private void initView(){ download = (Button) findViewById(R.id.download); download.setOnClickListener(this); upload = (Button) findViewById(R.id.upload); upload.setOnClickListener(this); info = (TextView) findViewById(R.id.info); select_file = (Button) findViewById(R.id.select_file); select_file.setOnClickListener(this); }@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {// TODO Auto-generated method stubsuper.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == 1) { Uri uri = data.getData(); // 接收用户所选文件的路径 info.setText("select: " + uri); // 在界面上显示路径 uploadFilePath = uri.getPath(); int last = uploadFilePath.lastIndexOf("/"); uploadFilePath = uri.getPath().substring(0, last+1); fileName = uri.getLastPathSegment(); } }} protected Dialog onCreateDialog(int id) { switch(id) { case PROGRESS_DIALOG: progressDialog = new ProgressDialog(UploadActivity.this); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setButton("暂停", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// TODO Auto-generated method stubuploadThread.closeLink();dialog.dismiss();}}); progressDialog.setMessage("正在上传..."); progressDialog.setMax(100); return progressDialog; default: return null; } } /** * 使用Handler给创建他的线程发送消息, * 匿名内部类 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { //获得上传长度的进度 int length = msg.getData().getInt("size"); progressDialog.setProgress(length); if(progressDialog.getProgress()==progressDialog.getMax())//上传成功 { progressDialog.dismiss(); Toast.makeText(UploadActivity.this, getResources().getString(R.string.upload_over), 1).show(); } } }; @Overridepublic void onClick(View v) {// TODO Auto-generated method stubResources r = getResources();switch(v.getId()){case R.id.select_file:Intent intent = new Intent();//设置起始目录和查找的类型 intent.setDataAndType(Uri.fromFile(new File("/sdcard")), "*/*");//"*/*"表示所有类型,设置起始文件夹和文件类型 intent.setClass(UploadActivity.this, FileBrowserActivity.class); startActivityForResult(intent, 1);break;case R.id.download:startActivity(new Intent(UploadActivity.this, SmartDownloadActivity.class));break;case R.id.upload: if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))//判断SDCard是否存在 { if(uploadFilePath == null){ Toast.makeText(UploadActivity.this, "还没设置上传文件", 1).show(); } System.out.println("uploadFilePath:"+uploadFilePath+" "+fileName); //取得SDCard的目录 File uploadFile = new File(new File(uploadFilePath), fileName); Log.i(TAG, "filePath:"+uploadFile.toString()); if(uploadFile.exists()) { showDialog(PROGRESS_DIALOG); info.setText(uploadFile+" "+ConstantValues.HOST+":"+ConstantValues.PORT); progressDialog.setMax((int) uploadFile.length());//设置长传文件的最大刻度 uploadThread = new UploadThread(UploadActivity.this, uploadFile, ConstantValues.HOST, ConstantValues.PORT); uploadThread.setListener(new UploadProgressListener() {@Overridepublic void onUploadSize(int size) {// TODO Auto-generated method stubMessage msg = new Message();msg.getData().putInt("size", size);handler.sendMessage(msg);}}); uploadThread.start(); } else { Toast.makeText(UploadActivity.this, "文件不存在", 1).show(); } } else { Toast.makeText(UploadActivity.this, "SDCard不存在!", 1).show(); }break;}}} UploadThread.java
package com.hao.upload;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.RandomAccessFile;import java.net.Socket;import android.content.Context;import android.util.Log;import com.hao.db.UploadLogService;import com.hao.util.StreamTool;public class UploadThread extends Thread {private static final String TAG = "UploadThread";/*需要上传文件的路径*/private File uploadFile;/*上传文件服务器的IP地址*/private String dstName;/*上传服务器端口号*/private int dstPort;/*上传socket链接*/private Socket socket;/*存储上传的数据库*/private UploadLogService logService; private UploadProgressListener listener;public UploadThread(Context context, File uploadFile, final String dstName,final int dstPort){this.uploadFile = uploadFile;this.dstName = dstName;this.dstPort = dstPort;logService = new UploadLogService(context);}public void setListener(UploadProgressListener listener) {this.listener = listener;}/** * 模拟断开连接 */public void closeLink(){try{if(socket != null) socket.close();}catch(IOException e){e.printStackTrace();Log.e(TAG, "close socket fail");}}@Overridepublic void run() {// TODO Auto-generated method stubtry {// 判断文件是否已有上传记录String souceid = logService.getBindId(uploadFile);// 构造拼接协议String head = "Content-Length=" + uploadFile.length()+ ";filename=" + uploadFile.getName() + ";sourceid="+ (souceid == null ? "" : souceid) + "%";// 通过Socket取得输出流socket = new Socket(dstName, dstPort);OutputStream outStream = socket.getOutputStream();outStream.write(head.getBytes());Log.i(TAG, "write to outStream");InputStream inStream = socket.getInputStream();// 获取到字符流的id与位置String response = StreamTool.readLine(inStream);Log.i(TAG, "response:" + response);String[] items = response.split(";");String responseid = items.substring(items.indexOf("=") + 1);String position = items.substring(items.indexOf("=") + 1);// 代表原来没有上传过此文件,往数据库添加一条绑定记录if (souceid == null) {logService.save(responseid, uploadFile);}RandomAccessFile fileOutStream = new RandomAccessFile(uploadFile, "r");// 查找上次传送的最终位置,并从这开始传送fileOutStream.seek(Integer.valueOf(position));byte[] buffer = new byte;int len = -1;// 初始化上传的数据长度int length = Integer.valueOf(position);while ((len = fileOutStream.read(buffer)) != -1) {outStream.write(buffer, 0, len);// 设置长传数据长度length += len;listener.onUploadSize(length);}fileOutStream.close();outStream.close();inStream.close();socket.close();// 判断上传完则删除数据if (length == uploadFile.length())logService.delete(uploadFile);} catch (Exception e) {e.printStackTrace();}}public interface UploadProgressListener{void onUploadSize(int size);}}
下面是多线程下载
http://dl.iteye.com/upload/attachment/601596/d8532800-9bcc-30f6-bf7a-f2cffdefcaff.jpg
SmartDownloadActivity.java
package com.hao;import java.io.File;import com.hao.R;import com.hao.R.id;import com.hao.R.layout;import com.hao.download.SmartFileDownloader;import com.hao.download.SmartFileDownloader.SmartDownloadProgressListener;import com.hao.util.ConstantValues;import android.app.Activity;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;/** ** @author Administrator **/public class SmartDownloadActivity extends Activity {private ProgressBar downloadbar;private TextView resultView;private String path = ConstantValues.DOWNLOAD_URL;SmartFileDownloader loader;private Handler handler = new Handler() {@Override// 信息public void handleMessage(Message msg) {switch (msg.what) {case 1:int size = msg.getData().getInt("size");downloadbar.setProgress(size);float result = (float) downloadbar.getProgress() / (float) downloadbar.getMax();int p = (int) (result * 100);resultView.setText(p + "%");if (downloadbar.getProgress() == downloadbar.getMax())Toast.makeText(SmartDownloadActivity.this, "下载成功", 1).show();break;case -1:Toast.makeText(SmartDownloadActivity.this, msg.getData().getString("error"), 1).show();break;}}};public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.download);Button button = (Button) this.findViewById(R.id.button);Button closeConn = (Button) findViewById(R.id.closeConn);closeConn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubif(loader != null){finish();}else{Toast.makeText(SmartDownloadActivity.this, "还没有开始下载,不能暂停", 1).show();}}});downloadbar = (ProgressBar) this.findViewById(R.id.downloadbar);resultView = (TextView) this.findViewById(R.id.result);resultView.setText(path);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {download(path, ConstantValues.FILE_PATH);} else {Toast.makeText(SmartDownloadActivity.this, "没有SDCard", 1).show();}}});}// 对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错private void download(final String path, final File dir) {new Thread(new Runnable() {@Overridepublic void run() {try {loader = new SmartFileDownloader(SmartDownloadActivity.this, path, dir, 3);int length = loader.getFileSize();// 获取文件的长度downloadbar.setMax(length);loader.download(new SmartDownloadProgressListener() {@Overridepublic void onDownloadSize(int size) {// 可以实时得到文件下载的长度Message msg = new Message();msg.what = 1;msg.getData().putInt("size", size);handler.sendMessage(msg);}});} catch (Exception e) {Message msg = new Message();// 信息提示msg.what = -1;msg.getData().putString("error", "下载失败");// 如果下载错误,显示提示失败!handler.sendMessage(msg);}}}).start();// 开始}}这个单个的下载线程
SmartDownloadThread.java
package com.hao.download;import java.io.File;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import android.util.Log;/** * 线程下载 * @author Administrator **/public class SmartDownloadThread extends Thread {private static final String TAG = "SmartDownloadThread";private File saveFile;private URL downUrl;private int block;/**下载开始位置 */private int threadId = -1;private int downLength;private boolean finish = false;private SmartFileDownloader downloader;public SmartDownloadThread(SmartFileDownloader downloader, URL downUrl,File saveFile, int block, int downLength, int threadId) {this.downUrl = downUrl;this.saveFile = saveFile;this.block = block;this.downloader = downloader;this.threadId = threadId;this.downLength = downLength;}@Overridepublic void run() {if (downLength < block) {// 未下载完成try {HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();http.setConnectTimeout(5 * 1000);http.setRequestMethod("GET");http.setRequestProperty("Accept","image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");http.setRequestProperty("Accept-Language", "zh-CN");http.setRequestProperty("Referer", downUrl.toString());http.setRequestProperty("Charset", "UTF-8");int startPos = block * (threadId - 1) + downLength;// 开始位置int endPos = block * threadId - 1;// 结束位置http.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);// 设置获取实体数据的范围http.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");http.setRequestProperty("Connection", "Keep-Alive");InputStream inStream = http.getInputStream();byte[] buffer = new byte;int offset = 0;print("Thread " + this.threadId + " start download from position " + startPos);RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");threadfile.seek(startPos);while ((offset = inStream.read(buffer, 0, 1024)) != -1) {threadfile.write(buffer, 0, offset);downLength += offset;downloader.update(this.threadId, downLength);downloader.saveLogFile();downloader.append(offset);}threadfile.close();inStream.close();print("Thread " + this.threadId + " download finish");this.finish = true;} catch (Exception e) {this.downLength = -1;print("Thread " + this.threadId + ":" + e);}}}private static void print(String msg) {Log.i(TAG, msg);}/** * 下载是否完成 * @return */public boolean isFinish() {return finish;}/** * 已经下载的内容大小 * @return 如果返回值为-1,代表下载失败 */public long getDownLength() {return downLength;}} 总得下载线程
SmartFileDownloader.java
package com.hao.download;import java.io.File;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import java.util.LinkedHashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;import java.util.regex.Matcher;import java.util.regex.Pattern;import com.hao.db.DownloadFileService;import android.content.Context;import android.util.Log;/** * 文件下载主程序 * @author Administrator **/public class SmartFileDownloader {private static final String TAG = "SmartFileDownloader";private Context context;private DownloadFileService fileService;/* 已下载文件长度 */private int downloadSize = 0;/* 原始文件长度 */private int fileSize = 0;/*原始文件名*/private String fileName;/* 线程数 */private SmartDownloadThread[] threads;/* 本地保存文件 */private File saveFile;/* 缓存各线程下载的长度 */private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();/* 每条线程下载的长度 */private int block;/* 下载路径 */private String downloadUrl;/** * 获取文件名 */public String getFileName(){return this.fileName;}/** * 获取线程数 */public int getThreadSize() {return threads.length;}/** * 获取文件大小 * @return */public int getFileSize() {return fileSize;}/** * 累计已下载大小 * @param size */protected synchronized void append(int size) {downloadSize += size;}/** * 更新指定线程最后下载的位置 * @param threadId 线程id * @param pos 最后下载的位置 */protected void update(int threadId, int pos) {this.data.put(threadId, pos);}/** * 保存记录文件 */protected synchronized void saveLogFile() {this.fileService.update(this.downloadUrl, this.data);}/** * 构建文件下载器 * @param downloadUrl 下载路径 * @param fileSaveDir 文件保存目录 * @param threadNum 下载线程数 */public SmartFileDownloader(Context context, String downloadUrl,File fileSaveDir, int threadNum) {try {this.context = context;this.downloadUrl = downloadUrl;fileService = new DownloadFileService(this.context);URL url = new URL(this.downloadUrl);if (!fileSaveDir.exists()) fileSaveDir.mkdirs();this.threads = new SmartDownloadThread;HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5 * 1000);conn.setRequestMethod("GET");conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");conn.setRequestProperty("Accept-Language", "zh-CN");conn.setRequestProperty("Referer", downloadUrl);conn.setRequestProperty("Charset", "UTF-8");conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");conn.setRequestProperty("Connection", "Keep-Alive");conn.connect();printResponseHeader(conn);if (conn.getResponseCode() == 200) {this.fileSize = conn.getContentLength();// 根据响应获取文件大小if (this.fileSize <= 0)throw new RuntimeException("Unkown file size ");fileName = getFileName(conn);this.saveFile = new File(fileSaveDir, fileName);/* 保存文件 */Map<Integer, Integer> logdata = fileService.getData(downloadUrl);if (logdata.size() > 0) {for (Map.Entry<Integer, Integer> entry : logdata.entrySet())data.put(entry.getKey(), entry.getValue());}//划分每个线程下载文件长度this.block = (this.fileSize % this.threads.length) == 0 ? this.fileSize / this.threads.length: this.fileSize / this.threads.length + 1;if (this.data.size() == this.threads.length) {for (int i = 0; i < this.threads.length; i++) {this.downloadSize += this.data.get(i + 1);}print("已经下载的长度" + this.downloadSize);}} else {throw new RuntimeException("server no response ");}} catch (Exception e) {print(e.toString());throw new RuntimeException("don't connection this url");}}/** * 获取文件名 */private String getFileName(HttpURLConnection conn) {String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1);//链接的最后一个/就是文件名if (filename == null || "".equals(filename.trim())) {// 如果获取不到文件名称for (int i = 0;; i++) {String mine = conn.getHeaderField(i);print("ConnHeader:"+mine+" ");if (mine == null)break;if ("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())) {Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase());if (m.find())return m.group(1);}}filename = UUID.randomUUID() + ".tmp";// 默认取一个文件名}return filename;}/** * 开始下载文件 ** @param listener * 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null * @return 已下载文件大小 * @throws Exception */public int download(SmartDownloadProgressListener listener)throws Exception {try {RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw");if (this.fileSize > 0)randOut.setLength(this.fileSize);randOut.close();URL url = new URL(this.downloadUrl);if (this.data.size() != this.threads.length) {this.data.clear();// 清除数据for (int i = 0; i < this.threads.length; i++) {this.data.put(i + 1, 0);}}for (int i = 0; i < this.threads.length; i++) {int downLength = this.data.get(i + 1);if (downLength < this.block && this.downloadSize < this.fileSize) { // 该线程未完成下载时,继续下载this.threads = new SmartDownloadThread(this, url,this.saveFile, this.block, this.data.get(i + 1), i + 1);this.threads.setPriority(7);this.threads.start();} else {this.threads = null;}}this.fileService.save(this.downloadUrl, this.data);boolean notFinish = true;// 下载未完成while (notFinish) {// 循环判断是否下载完毕Thread.sleep(900);notFinish = false;// 假定下载完成for (int i = 0; i < this.threads.length; i++) {if (this.threads != null && !this.threads.isFinish()) {notFinish = true;// 下载没有完成if (this.threads.getDownLength() == -1) {// 如果下载失败,再重新下载this.threads = new SmartDownloadThread(this,url, this.saveFile, this.block, this.data.get(i + 1), i + 1);this.threads.setPriority(7);this.threads.start();}}}if (listener != null)listener.onDownloadSize(this.downloadSize);}fileService.delete(this.downloadUrl);} catch (Exception e) {print(e.toString());throw new Exception("file download fail");}return this.downloadSize;}/** * 获取Http响应头字段 ** @param http * @return */public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) {Map<String, String> header = new LinkedHashMap<String, String>();for (int i = 0;; i++) {String mine = http.getHeaderField(i);if (mine == null)break;header.put(http.getHeaderFieldKey(i), mine);}return header;}/** * 打印Http头字段 ** @param http */public static void printResponseHeader(HttpURLConnection http) {Map<String, String> header = getHttpResponseHeader(http);for (Map.Entry<String, String> entry : header.entrySet()) {String key = entry.getKey() != null ? entry.getKey() + ":" : "";print(key + entry.getValue());}}// 打印日志private static void print(String msg) {Log.i(TAG, msg);}public interface SmartDownloadProgressListener {public void onDownloadSize(int size);}}
好了这里只是将主要的代码分享出来,主要是为了了解他的基本流程,然后自己可以扩展,和优化
页:
[1]