1. 程式人生 > 其它 >c# 檔案的斷點續傳

c# 檔案的斷點續傳

一、開篇描述

本篇部落格所描述的斷點續傳功能是基於c#語言,伺服器端採用.net mvc框架,客戶端採用winform框架。

本篇部落格實現斷點續傳功能的基本思路:1)伺服器端是把接收到的檔案流,追加到已有的檔案;2)客戶端是把檔案流截段上傳;

其實,任何一種計算機語言基於這個思路(web客戶端JavaScript目前例外),都可以實現斷點續傳的功能。

二、伺服器端

namespace MvcApplicationUpload.Controllers
{
    public class HomeController : Controller
    {
        /// <summary>
/// Index /// </summary> /// <returns></returns> public ActionResult Index() { //string ip = Request.UserHostAddress; //string port = Request.ServerVariables["REMOTE_PORT"].ToString(); return View(); }
/// <summary> /// Test /// </summary> /// <returns></returns> public ActionResult Test() { return PartialView(); } /// <summary> /// 獲取續傳點。 /// </summary> /// <param name="md5Name"></param>
/// <returns></returns> public string GetFileResumePoint(string md5Name) { var saveFilePath = Server.MapPath("~/Images/") + md5Name; if (System.IO.File.Exists(saveFilePath)) { var fs = System.IO.File.OpenWrite(saveFilePath); var fsLength = fs.Length.ToString(); fs.Close(); return fsLength; } return "0"; } /// <summary> /// 檔案續傳。 /// </summary> /// <returns></returns> [HttpPost] public HttpResponseMessage FileResume() { var md5Name = Request.QueryString["md5Name"]; var fileStream = Request.InputStream; var contentRange = Request.Headers["Content-Range"];//contentRange樣例:bytes 0-1000/5000 SaveAs(Server.MapPath("~/Images/") + md5Name, fileStream, contentRange); return new HttpResponseMessage(HttpStatusCode.OK); } /// <summary> /// 給已有檔案追加檔案流。 /// </summary> /// <param name="saveFilePath"></param> /// <param name="stream"></param> private void SaveAs(string saveFilePath, System.IO.Stream stream, string contentRange) { //接收到的碎片位元組流資訊。 long startPosition = 0; long endPosition = 0; if (!string.IsNullOrEmpty(contentRange)) { contentRange = contentRange.Replace("bytes", "").Trim(); contentRange = contentRange.Substring(0, contentRange.IndexOf("/")); string[] ranges = contentRange.Split('-'); startPosition = long.Parse(ranges[0]); endPosition = long.Parse(ranges[1]); } else { return; } //預設寫針位置。 System.IO.FileStream fs; long writeStartPosition = 0; if (System.IO.File.Exists(saveFilePath)) { fs = System.IO.File.OpenWrite(saveFilePath); writeStartPosition = fs.Length; } else { fs = new System.IO.FileStream(saveFilePath, System.IO.FileMode.Create); } //調整寫針位置。 if (writeStartPosition >= endPosition) { fs.Close(); return; } else if (writeStartPosition < startPosition) { fs.Close(); return; } else if (writeStartPosition > startPosition && writeStartPosition < endPosition) { writeStartPosition = startPosition; } fs.Seek(writeStartPosition, System.IO.SeekOrigin.Current); //向檔案追加檔案流。 byte[] nbytes = new byte[512]; int nReadSize = 0; while ((nReadSize = stream.Read(nbytes, 0, 512)) > 0) { fs.Write(nbytes, 0, nReadSize); } fs.Close(); } } }

三、客戶端

1 介面如下:

2 程式碼如下:

namespace WindowsFormsApplicationUpload
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// 定義委託。
        /// </summary>
        /// <param name="text"></param>
        delegate void ChangeText(string text);
 
        /// <summary>
        /// 暫停標記。
        /// </summary>
        int flag = 0;
 
        /// <summary>
        /// 上傳的檔案路徑。
        /// </summary>
        string filePath;
 
        /// <summary>
        /// 上傳的檔案md5值。
        /// </summary>
        string md5Str;
 
        /// <summary>
        /// 上傳的檔案續傳點。
        /// </summary>
        long startPoint;
 
        public Form1()
        {
            InitializeComponent();
        }
 
        /// <summary>
        /// 委託方法。
        /// </summary>
        /// <param name="text"></param>
        public void ChangeLabelText(string text)
        {
            this.label1.Text = text;
        }
 
        /// <summary>
        /// 得到檔案的MD5值。
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string GetFileMd5(string filePath)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            Byte[] hashBytes = md5.ComputeHash(fs);
            fs.Close();
            return BitConverter.ToString(hashBytes).Replace("-", "");
        }
 
        /// <summary>
        /// 得到檔案的續傳點。
        /// </summary>
        /// <param name="md5Str"></param>
        /// <param name="fileExtension"></param>
        /// <returns></returns>
        public long GetFileResumePoint(string md5Str, string fileExtension)
        {
            System.Net.WebClient webClient = new System.Net.WebClient();
            string urlStr = "http://127.0.0.1/Home/GetFileResumePoint?md5Name=" + md5Str + fileExtension;
            byte[] infoBytes = webClient.DownloadData(urlStr);
            string result = System.Text.Encoding.UTF8.GetString(infoBytes);
            webClient.Dispose();
            if (string.IsNullOrEmpty(result))
            {
                return 0;
            }
            return Convert.ToInt64(result);
        }
 
        public void ResumeFileThread()
        {
            MessageBox.Show(ResumeFile("http://127.0.0.1/Home/FileResume", filePath, startPoint, 1024 * 128, md5Str));
        }
 
        public string ResumeFile(string hostUrl, string filePath, long startPoint, int byteCount, string md5Str)
        {
            string result = "上傳成功!";
            if (startPoint < 0)
            {
                result = "指標有誤!";
                return result;
            }
            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            long fileLength = fs.Length;
            if (startPoint >= fileLength)
            {
                result = "檔案秒傳!";
                return result;
            }
 
            byte[] data;
            System.Net.WebClient webClient = new System.Net.WebClient();
            BinaryReader bReader = new BinaryReader(fs);
            try
            {
                #region 續傳處理
                if (startPoint >= 1 && startPoint < fileLength)
                {
                    fs.Seek(startPoint, SeekOrigin.Current);
                }
                #endregion
                #region 分割檔案上傳
                int step = byteCount;
                for (; startPoint < fileLength; startPoint += step)
                {
                    if (startPoint + byteCount > fileLength)
                    {
                        step = Convert.ToInt32(fileLength - startPoint);
                        data = new byte[step];
                        bReader.Read(data, 0, step);
                    }
                    else
                    {
                        data = new byte[byteCount];
                        bReader.Read(data, 0, byteCount);
                    }
                    webClient.Headers.Set(HttpRequestHeader.ContentRange, "bytes " + startPoint + "-" + (startPoint + step) + "/" + fs.Length);
                    webClient.UploadData(hostUrl + "?md5Name=" + md5Str + Path.GetExtension(filePath), "POST", data);
                    this.BeginInvoke(new ChangeText(ChangeLabelText), (startPoint + step) + "/" + fileLength);
                pause:
                    if (flag == 1)
                    {
                        Thread.Sleep(1000);
                        goto pause;
                    }
                }
                #endregion
            }
            catch (Exception ex)
            {
                result = ex.Message;
            }
            finally
            {
                bReader.Close();
                fs.Close();
                webClient.Dispose();
            }
            return result;
        }
 
        /// <summary>
        /// 上傳按鈕點選事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                filePath = openFileDialog.FileName;
                md5Str = GetFileMd5(filePath);
                startPoint = GetFileResumePoint(md5Str, Path.GetExtension(filePath));
                ThreadStart ts = new ThreadStart(ResumeFileThread);
                Thread t = new Thread(ts);
                t.Start();
            }
        }
 
        /// <summary>
        /// 暫停按鈕點選事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            flag = 1;
        }
 
        /// <summary>
        /// 繼續按鈕點選事件。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            flag = 0;
        }
 
        /// <summary>
        /// 拖拽上傳。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Drag(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
                MessageBox.Show("準備上傳" + files.Length + "個檔案!");
                foreach (string file in files)
                {
                    MessageBox.Show("開始上傳檔案:" + file);
                    filePath = file;
                    md5Str = GetFileMd5(filePath);
                    startPoint = GetFileResumePoint(md5Str, Path.GetExtension(filePath));
                    ThreadStart ts = new ThreadStart(ResumeFileThread);
                    Thread t = new Thread(ts);
                    t.Start();
                }
            }
        }
 
        private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            //Form1_Drag(sender, e);
        }
 
        private void Form1_DragOver(object sender, DragEventArgs e)
        {
            Form1_Drag(sender, e);
        }
 
    }
}