C#集成FastDFS断点续传


C#集成FastDFS断点续传

参考

.net版本FastDFS客户端v5.05

https://github.com/zhouyh362329/fastdfs.client.net

FastDFS环境准备

http://www.cnblogs.com/ddrsql/p/7118695.html

webuploader

http://fex.baidu.com/webuploader/

普通方式下载

非断点续传方式下载。

        /// 
        /// 普通下载
        /// 
        public void Download()
        {
            string fileName = "下载测试test.txt";
            string filePath = Server.MapPath("/File/test.txt");

            System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath);

            if (fileInfo.Exists == true)
            {
                const long ChunkSize = 10240;
                byte[] buffer = new byte[ChunkSize];

                Response.Clear();
                System.IO.FileStream iStream = System.IO.File.OpenRead(filePath);
                long dataLengthToRead = iStream.Length;//获取下载的文件总大小 
                Response.ContentEncoding = Encoding.UTF8;
                Response.ContentType = "application/octet-stream";
                Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
                while (dataLengthToRead > 0 && Response.IsClientConnected)
                {
                    Thread.Sleep(100);
                    int lengthRead = iStream.Read(buffer, 0, Convert.ToInt32(ChunkSize));//读取的大小 
                    Response.OutputStream.Write(buffer, 0, lengthRead);
                    Response.Flush();
                    dataLengthToRead -= lengthRead;
                }
                Response.Close();
            }
        }

下载完成前客户端不知道文件大小。

MVC FastDFS 断点续传方式下载

断点续传几个关键Header属性:

Request

If-Range、Range

Response

StatusCode = 206

Accept-Ranges、ETag、Content-Length、Content-Range

        /// 
        /// FastDFS下载文件,断点续传
        /// 
        /// 
        public bool DownloadByFDFS()
        {
            string group_name = "group1";
            string remote_filename = "M00/00/00/CgEG1FlWChOEHXNFAAAAAEFinIM178.txt";
            string fileName = "测试.txt";
            long length = 2878186;

            long speed = 1024 * 100;
            bool ret = true;
            try
            {
                #region--验证:HttpMethod,请求的文件是否存在
                switch (Request.HttpMethod.ToUpper())
                { //目前只支持GET和HEAD方法
                    case "GET":
                    case "HEAD":
                        break;
                    default:
                        Response.StatusCode = 501;
                        return false;
                }
                #endregion

                #region 定义局部变量
                long startBytes = 0;
                int packSize = 1024 * 10; //分块读取,每块10K bytes
                long fileLength = length;// myFile.Length;

                int sleep = (int)Math.Ceiling(1000.0 * packSize / speed);//毫秒数:读取下一数据块的时间间隔
                string lastUpdateTiemStr = "2017-07-18 11:57:54";
                string eTag = HttpUtility.UrlEncode(fileName, Encoding.UTF8) + lastUpdateTiemStr;//便于恢复下载时提取请求头;
                #endregion

                #region--验证:文件是否太大,是否是续传,且在上次被请求的日期之后是否被修改过--------------
                //if (myFile.Length > Int32.MaxValue)
                //{//-------文件太大了-------
                //    Response.StatusCode = 413;//请求实体太大
                //    return false;
                //}

                if (Request.Headers["If-Range"] != null)//对应响应头ETag:文件名+文件最后修改时间
                {
                    //----------上次被请求的日期之后被修改过--------------
                    if (Request.Headers["If-Range"].Replace("\"", "") != eTag)
                    {//文件修改过
                        Response.StatusCode = 412;//预处理失败
                        return false;
                    }
                }
                #endregion

                try
                {
                    #region -------添加重要响应头、解析请求头、相关验证-------------------
                    Response.Clear();
                    Response.Buffer = false;
                    //Response.AddHeader("Content-MD5", GetMD5Hash(myFile));//用于验证文件
                    Response.AddHeader("Accept-Ranges", "bytes");//重要:续传必须
                    Response.AppendHeader("ETag", "\"" + eTag + "\"");//重要:续传必须
                    Response.AppendHeader("Last-Modified", lastUpdateTiemStr);//把最后修改日期写入响应                
                    Response.ContentType = "application/octet-stream";//MIME类型:匹配任意文件类型
                    Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);//s HttpUtility.UrlEncode(fileName, Encoding.UTF8).Replace("+", "%20"));

                    Response.AddHeader("Connection", "Keep-Alive");
                    Response.ContentEncoding = Encoding.UTF8;
                    if (Request.Headers["Range"] != null)
                    {//------如果是续传请求,则获取续传的起始位置,即已经下载到客户端的字节数------
                        Response.StatusCode = 206;//重要:续传必须,表示局部范围响应。初始下载时默认为200
                        string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });//"bytes=1474560-"
                        startBytes = Convert.ToInt64(range[1]);//已经下载的字节数,即本次下载的开始位置  
                        if (startBytes < 0 || startBytes >= fileLength)
                        {//无效的起始位置
                            return false;
                        }
                    }
                    Response.AddHeader("Content-Length", (fileLength - startBytes).ToString());
                    if (startBytes > 0)
                    {//------如果是续传请求,告诉客户端本次的开始字节数,总长度,以便客户端将续传数据追加到startBytes位置后----------
                        Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - 1, fileLength));
                    }
                    #endregion

                    #region -------向客户端发送数据块-------------------
                    //binaryReader.BaseStream.Seek(startBytes, SeekOrigin.Begin);
                    int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / packSize);//分块下载,剩余部分可分成的块数
                    for (int i = 0; i < maxCount && Response.IsClientConnected; i++)
                    {//客户端中断连接,则暂停
                        if (fileLength - startBytes < packSize)
                            packSize = 0;
                        byte[] fdfs = client.download_file(group_name, remote_filename, startBytes, packSize);
                        Response.BinaryWrite(fdfs);
                        Response.Flush();
                        startBytes = startBytes + packSize;
                        if (sleep > 1) Thread.Sleep(sleep);
                    }
                    #endregion
                }
                catch
                {
                    ret = false;
                }
                finally
                {
                }
            }
            catch
            {
                ret = false;
            }
            return ret;
        }

断点续传方式下载文件效果:

MVC FastDFS断点续传方式上传

文件分段上传至FastDFS首次使用upload_appender_file,之后使用append_file追加。

        public string Upload(HttpPostedFileBase file)
        {
            string group = "";
            string path = "";
            string tempPath = Server.MapPath("~/File/temp.txt");
            byte[] buffer = new byte[file.ContentLength];
            System.IO.Stream fs = (System.IO.Stream)file.InputStream;
            fs.Read(buffer, 0, file.ContentLength);

            NameValuePair[] meta_list = new NameValuePair[4];
            meta_list[0] = new NameValuePair("width", "800");
            meta_list[1] = new NameValuePair("heigth", "600");
            meta_list[2] = new NameValuePair("bgcolor", "#FFFFFF");
            meta_list[3] = new NameValuePair("author", "Mike");
            if (Request.Params["chunk"] == "0" || Request.Params["chunk"] == null)
            {
                string ext = Path.GetExtension(file.FileName).Replace(".", "");
                //fastdfs上传
                var results = client.upload_appender_file(buffer, ext, meta_list);
                group = results[0];
                path = results[1];
                //临时存储group、path
                StreamWriter sw = new StreamWriter(tempPath, false);
                sw.WriteLine(group);
                sw.WriteLine(path);
                sw.Close();

            }
            else
            {
                if (System.IO.File.Exists(tempPath))
                {
                    StreamReader sr = new StreamReader(tempPath);
                    int i = 0;
                    string strReadline = string.Empty;
                    //读取group、path
                    while ((strReadline = sr.ReadLine()) != null)
                    {
                        if (i == 0)
                            group = strReadline;
                        else if (i == 1)
                            path = strReadline;
                        i++;
                    }
                    sr.Close();
                }
                //文件续传,fastdfs追加上传
                var flag = client.append_file(group, path, buffer);
            }

            return "{\"data\":{\"chunked\" : true, \"ext\" : \"exe\"}}";
        }

        public string GetMaxChunk(string md5)
        {
            //根据实际存储介质返回文件上传终止的段
            return "{\"data\":0}";
        }

上传pconline1477535934501.zip文件测试。

 

上传完成后查看/File/temp.txt文件记录。

到相应的group查看:

WEBAPI FastDFS断点续传方式下载

WEBAPI中PushStreamContent 推送

        /// 
        /// PushStreamContent 推送
        /// FDFS文件,断点续传
        /// 
        /// 
        public HttpResponseMessage GetFileFDFS()
        {
            HttpResponseMessage response = new HttpResponseMessage();
            if (Request.Headers.IfRange != null && Request.Headers.IfRange.EntityTag.ToString().Replace("\"", "") != NowTime)
            {
                response.StatusCode = HttpStatusCode.PreconditionFailed;
                return response;
            }

            string group_name = "group1";
            string remote_filename = "M00/00/00/CgEG1FlWChOEHXNFAAAAAEFinIM178.txt";
            string fileName = "测试.txt";
            long fileLength = 2878186;

            long speed = 1024 * 100;
            long packSize = 1024 * 10;
            int sleep = (int)Math.Ceiling(1000.0 * packSize / speed);//毫秒数:读取下一数据块的时间间隔
            ContentInfo contentInfo = GetContentInfoFromRequest(this.Request, fileLength);
            Action pushContentAction = (outputStream, content, context) =>
            {
                try
                {
                    int length = Convert.ToInt32((fileLength - 1) - contentInfo.From) + 1;
                    while (length > 0 && packSize > 0)
                    {
                        byte[] fdfs = client.download_file(group_name, remote_filename, contentInfo.From, Math.Min(length, packSize));
                        outputStream.Write(fdfs, 0, fdfs.Length);
                        contentInfo.From = contentInfo.From + fdfs.Length;
                        length -= fdfs.Length;
                        if (sleep > 1) Thread.Sleep(sleep);
                    }
                    //int maxCount = (int)Math.Ceiling((fileLength - contentInfo.From + 0.0) / packSize);//分块下载,剩余部分可分成的块数
                    //for (int i = 0; i < maxCount && HttpContext.Current.Response.IsClientConnected; i++)
                    //{
                    //    if (fileLength - contentInfo.From < packSize)
                    //        packSize = 0;
                    //    byte[] fdfs = client.download_file(group_name, remote_filename, contentInfo.From, packSize);
                    //    outputStream.Write(fdfs, 0, fdfs.Length);
                    //    contentInfo.From = contentInfo.From + fdfs.Length;
                    //    if (sleep > 1) Thread.Sleep(sleep);
                    //}
                }
                catch (HttpException ex)
                {
                    throw ex;
                }
                finally
                {
                    outputStream.Close();
                }
            };
            response.Content = new PushStreamContent(pushContentAction, new MediaTypeHeaderValue(MimeType));
            //response.Content = new PushStreamContent(pushContentAction);
            SetResponseHeaders(response, contentInfo, fileLength, fileName);
            return response;
        }

demo下载地址:https://pan.baidu.com/s/1i5rDs49