• 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/

    普通方式下载

    非断点续传方式下载。

            /// <summary>
            /// 普通下载
            /// </summary>
            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

            /// <summary>
            /// FastDFS下载文件,断点续传
            /// </summary>
            /// <returns></returns>
            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 推送

            /// <summary>
            /// PushStreamContent 推送
            /// FDFS文件,断点续传
            /// </summary>
            /// <returns></returns>
            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<Stream, HttpContent, TransportContext> 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

     

     

  • 相关阅读:
    改善用户体念:jsp+Ajax实现实时上传,删除,导入Excel进度条
    hibernate+proxool的数据库连接池配置方法
    Js事件对象
    关于java数组拷贝的性能
    resin连接池配置
    java 动态编译源代码
    Java中对字符串进行加密和解密
    内存数据库H2 Mixed Mode布署
    ActiveX控件的另类免费签名法
    对PreparedStatement、Statement的一点总结
  • 原文地址:https://www.cnblogs.com/ddrsql/p/7199894.html
Copyright © 2020-2023  润新知