断点续传、多线程上载【转:http://www.cnblogs.com/dlwang2002/archive/2008/09/12/1290017.html】

现在已经有很多断点续传、多线程下载的软件了,比如网际快车等等。下面设计的程序是“断点续传、多线程上载”。

 

缘起:客户每天都有大量文件上传服务器。这些文件很多,并且体积挺大,FTP有时候会出一些问题,导致传递失败,要重新上传。

 

基本解决方案:

1:把文件分割成块,每次只是传递一个文件块。

2:一个文件可以起多个发送任务(线程),同时发送。

3:记录文件发送状态,在网络出现问题时(或者客户端意外终止),知道上次发送文件大小和位置指针。再重新链接以后,继续发送。

 

对象和线程

这里面涉及到一个显示窗体form1,有timer可以随时更新发送状态;一个上传类Uploader(对应于一个文件);Task对象(也就是一个文件);FileThunk对象(每一个任务,对应于一个线程);WebService接受文件类。

发送状态需要记录在数据库。测试状态下,数据记录在xml文件。基本格式如下:

      

Codetasks>
  
<task name="contact.txt" percentage="" totalSeconds="" localFile="" remoteFile="" fileSize="">
   
<thread name="thread1" begin="" end="" lastTime=""></thread>

  
</task>
</tasks>

数据表结构也基本类似这样。

 

核心代码

1form1. 这个主要是显示。主要函数是 添加任务(Task);更新任务状态

 
浏览文件,创建任务   private void btn_browse_Click(object sender, EventArgs e)
 2         {
 3             //browse to select files
 4             OpenFileDialog ofd = new OpenFileDialog();
 5             ofd.Multiselect = true;
 6             DialogResult dr= ofd.ShowDialog();
 7 
 8             _taskList = new ArrayList();
 9             for (int i = 0; i < ofd.FileNames.Length; i++)
10             {
11                 Task t = new Task();
12                 t.FileChunkCount = Convert.ToInt32(this.txt_threadsPerFile.Text);
13                 t.LocalFile = ofd.FileNames[i];
14                 t.RemoteFile = Path.GetFileName(t.LocalFile);
15                 t.Name = Path.GetFileName(t.LocalFile);
16                 //t.Percentage=0;
17                 t.TotalSeconds=0;
18 
19                 t.Init(); //init, split the file to upload
20 
21                 _taskList.Add(t);
22                 
23             }
24             //show in UI
25             this.dataGridView1.DataSource = GetUpdatedStatusAsDT(_taskList,0);
26         }
显示和更新状态,返回DataTable   private DataTable GetUpdatedStatusAsDT(ArrayList list, int seconds)
 2         {
 3             DataTable dt = new DataTable();
 4             dt.Columns.Add("Name");
 5             dt.Columns.Add("FileSize");
 6             dt.Columns.Add("Percentage");
 7             dt.Columns.Add("TotalSeconds");
 8             dt.Columns.Add("LocalFile");
 9             dt.Columns.Add("RemoteFile");
10 
11             for (int i = 0; i < list.Count; i++)
12             {
13                 //update task status to db
14                 Task t = (list[i] as Task);
15                 t.TotalSeconds += seconds;//running time
16                 t.Save();//save the status to db, so next time can load the status to continue.
17 
18                 DataRow dr = dt.NewRow();
19                 dr["Name"= t.Name;
20                 dr["FileSize"= t.FileSize;
21                 dr["Percentage"= t.Percentage;
22                 dr["TotalSeconds"= t.TotalSeconds;
23                 dr["LocalFile"= t.LocalFile;
24                 dr["RemoteFile"= t.RemoteFile;
25 
26                 dt.Rows.Add(dr);
27             }
28             return dt;
29         }
执行任务    private void btn_run_Click(object sender, EventArgs e)
 2         {
 3             //timmer to show the status
 4             Timer timer = new Timer();
 5             timer.Tick += new EventHandler(timer_Tick);
 6             timer.Interval = 2 * 1000//every 2 seconds
 7             timer.Enabled = true;
 8             timer.Start();
 9             //
10             //continue
11             //find task from datasource (DB or xml) and continue
12             //ArrayList tasks = Task.LoadTasks();
13             //
14             ArrayList tasks = _taskList;  // get the task list.
15             //
16             for (int i = 0; i < tasks.Count; i++)
17             {
18                 Uploader uld = new Uploader(tasks[i] as Task);
19                 uld.Strat();
20             }
21         }
22 


2:Uploader的核心代码:主要是启动线程。

Code   public void Strat()
 2         {
 3             //_dataReceiver.Open(this._task.RemoteFile);//init webservice
 4 
 5             #region test
 6             //if (!File.Exists(_task.RemoteFile))
 7             //{
 8             //    FileInfo fi = new FileInfo(_task.LocalFile);
 9 
10             //    FileStream fst = new FileStream(_task.RemoteFile, FileMode.CreateNew);
11 
12             //    BinaryWriter w = new BinaryWriter(fst);
13             //    byte[] ab = new byte[fi.Length];
14             //    w.Write(ab);
15             //    w.Close();
16             //    fst.Close();
17             //}
18             // 
19             #endregion
20            
21             //main code. find each fileChunk, running in seperate thread
22             for (int i = 0; i < _task.FileChunks.Count; i++)
23             {
24                 FileChunk fc = (_task.FileChunks[i] as FileChunk);
25                 Thread thread = new Thread(new ThreadStart(fc.Upload));
26                 fc.RunningThread = thread;//asign thread to the fileChucks, to stop the task
27                 thread.Name = _task.Name + "_" + i.ToString();
28                 thread.Start();
29             }
30         }


3:Task类的主要代码:主要是初始化任务,其他诸如和对数据的保存/提取 

Code    public void Init()
 2         {
 3             //create new
 4            this.FileChunks = new ArrayList();
 5 
 6             FileInfo fi = new FileInfo(LocalFile);
 7             FileSize = fi.Length;
 8 
 9             long block = FileSize / this.FileChunkCount;
10             long index = 0;
11             for (int i = 0; i < this.FileChunkCount; i++)
12             {
13                 FileChunk fc = new FileChunk(this);
14                 fc.Begin = index;
15                 if (i==this.FileChunkCount-1)
16                     block = FileSize - index;
17                 index += block;
18                 fc.End = index;
19                 fc.LastTime = DateTime.Now;
20 
21                 this.FileChunks.Add(fc);
22             }
23             //
24  
25         }


4:FileChunk的核心代码:主要是如何上传文件

Code   /// <summary>
 2         /// main function
 3         /// </summary>
 4         public void Upload()
 5         {
 6             Stream fs = File.OpenRead(_task.LocalFile);
 7 
 8             long len = 2 * 64 * 1024;// 2 * 64 * 1024;
 9  
10             while (Begin < End)
11             {
12                 if (End - Begin < len)
13                     len = End - Begin;
14                 byte[] data = new byte[len];
15                 fs.Position = Begin;
16                 fs.Read(data, 0, (int)len);
17              
18                 #region local test 
19                 //Stream fs2 = File.OpenWrite(_task.RemoteFile);
20                 //fs2.Position = Begin;
21                 //fs2.Write(data, 0, (int)len);
22                 //fs2.Flush();
23                 //fs2.Close(); 
24                 //
25                 #endregion
26                
27                 //call webservice to receive the data. this can be a socket also.
28                 string res = this._task.DataReceiver.Receive(_task.RemoteFile, Begin, len, data);
29                 if (res == "ok")
30                 {
31                     //
32                     Begin += len;
33                     _task.Save();
34                 }
35                 else
36                 {
37                     //wait for next while, 
38                 }
39 
40             }
41         }



5:WebService的核心代码

Codepublic string Receive(string file, long begin, long len, byte[] data)
    {
        _fileName 
= AppDomain.CurrentDomain.BaseDirectory + """tmp""" + file;
        
try
        {
            _fs 
= File.OpenWrite(_fileName);
            _fs.Position 
= begin;
            _fs.Write(data, 
0, (int)len);
            _fs.Flush();
            _fs.Close();
            _fs 
= null;
            
return "ok";
        }
        
catch (Exception ex)
        {
            
string s = ex.Message;
            
return "retry";
        }

    }



  小结

技术难度不大;现实实用;上传速度尚可。

  程序运行截图一个
file_uploader.jpg

posted on 2008-09-14 16:00  草原和大树  阅读(655)  评论(0编辑  收藏  举报