断点续传和下载原理分析

您所在的位置:网站首页 GLC43声浪交警查吗 断点续传和下载原理分析

断点续传和下载原理分析

#断点续传和下载原理分析| 来源: 网络整理| 查看: 265

断点续传和断点下载都是用的RandomAccessFile, 它具有移动指定的文件大小的位置的功能seek 。

断点续传是由服务器给客户端一个已经上传的位置标记position,然后客户端再将文件指针移动到相应的position,通过输入流将文件剩余部分读出来传输给服务器

断点下载 是由客户端告诉服务器已经下载的大小,然后服务器会将指针移动到相应的position,继续读出,把文件返回给客户端。 当然为了下载的更快一下,也可以多线程下载,那么基本实现就是给每个线程分配固定的字节的文件,分别去读

 

首先是文件上传,这个要用到服务器

关键代码:

 FileServer.java

 

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 datas = new HashMap();//存放断点数据,以后改为数据库存放        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;           }           @Override           public 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[0].substring(items[0].indexOf("=")+1);                       String filename = items[1].substring(items[1].indexOf("=")+1);                       String sourceid = items[2].substring(items[2].indexOf("=")+1);                             //生成资源id,如果需要唯一性,可以采用UUID                       long 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[1024];                       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);        }           }  

 由于在上面的流程图中已经进行了详细的分析,我在这儿就不讲了,只是在存储数据的时候服务器没有用数据库去存储,这儿只是为了方便,所以要想测试断点上传,服务器是不能停的,否则数据就没有了,在以后改进的时候应该用数据库去存储数据。

文件上传客户端:

关键代码:

UploadActivity.java

 

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);       }              @Override       protected void onActivityResult(int requestCode, int resultCode, Intent data) {           // TODO Auto-generated method stub           super.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() {                   @Override                   public void onClick(DialogInterface dialog, int which) {                       // TODO Auto-generated method stub                       uploadThread.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();                 }             }         };             @Override       public void onClick(View v) {           // TODO Auto-generated method stub           Resources 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() {                                                              @Override                               public void onUploadSize(int size) {                                   // TODO Auto-generated method stub                                   Message 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

 

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");           }       }          @Override       public void run() {           // TODO Auto-generated method stub           try {               // 判断文件是否已有上传记录               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[0].substring(items[0].indexOf("=") + 1);               String position = items[1].substring(items[1].indexOf("=") + 1);               // 代表原来没有上传过此文件,往数据库添加一条绑定记录               if (souceid == null) {                   logService.save(responseid, uploadFile);               }               RandomAccessFile fileOutStream = new RandomAccessFile(uploadFile, "r");               // 查找上次传送的最终位置,并从这开始传送               fileOutStream.seek(Integer.valueOf(position));               byte[] buffer = new byte[1024];               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);       }   }  

  下面是多线程下载

SmartDownloadActivity.java

 

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() {                              @Override               public void onClick(View v) {                   // TODO Auto-generated method stub                   if(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() {               @Override               public 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() {               @Override               public void run() {                   try {                       loader = new SmartFileDownloader(SmartDownloadActivity.this, path, dir, 3);                       int length = loader.getFileSize();// 获取文件的长度                       downloadbar.setMax(length);                       loader.download(new SmartDownloadProgressListener() {                           @Override                           public 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

 

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;       }          @Override       public void run() {           if (downLength 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3