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 = ;
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 > && Response.IsClientConnected)
{
Thread.Sleep();
int lengthRead = iStream.Read(buffer, , Convert.ToInt32(ChunkSize));//读取的大小
Response.OutputStream.Write(buffer, , 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 = ; long speed = * ;
bool ret = true;
try
{
#region--验证:HttpMethod,请求的文件是否存在
switch (Request.HttpMethod.ToUpper())
{ //目前只支持GET和HEAD方法
case "GET":
case "HEAD":
break;
default:
Response.StatusCode = ;
return false;
}
#endregion #region 定义局部变量
long startBytes = ;
int packSize = * ; //分块读取,每块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 = ;//预处理失败
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 = ;//重要:续传必须,表示局部范围响应。初始下载时默认为200
string[] range = Request.Headers["Range"].Split(new char[] { '=', '-' });//"bytes=1474560-"
startBytes = Convert.ToInt64(range[]);//已经下载的字节数,即本次下载的开始位置
if (startBytes < || startBytes >= fileLength)
{//无效的起始位置
return false;
}
}
Response.AddHeader("Content-Length", (fileLength - startBytes).ToString());
if (startBytes > )
{//------如果是续传请求,告诉客户端本次的开始字节数,总长度,以便客户端将续传数据追加到startBytes位置后----------
Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, fileLength - , fileLength));
}
#endregion #region -------向客户端发送数据块-------------------
//binaryReader.BaseStream.Seek(startBytes, SeekOrigin.Begin);
int maxCount = (int)Math.Ceiling((fileLength - startBytes + 0.0) / packSize);//分块下载,剩余部分可分成的块数
for (int i = ; i < maxCount && Response.IsClientConnected; i++)
{//客户端中断连接,则暂停
if (fileLength - startBytes < packSize)
packSize = ;
byte[] fdfs = client.download_file(group_name, remote_filename, startBytes, packSize);
Response.BinaryWrite(fdfs);
Response.Flush();
startBytes = startBytes + packSize;
if (sleep > ) 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, , file.ContentLength); NameValuePair[] meta_list = new NameValuePair[];
meta_list[] = new NameValuePair("width", "");
meta_list[] = new NameValuePair("heigth", "");
meta_list[] = new NameValuePair("bgcolor", "#FFFFFF");
meta_list[] = new NameValuePair("author", "Mike");
if (Request.Params["chunk"] == "" || Request.Params["chunk"] == null)
{
string ext = Path.GetExtension(file.FileName).Replace(".", "");
//fastdfs上传
var results = client.upload_appender_file(buffer, ext, meta_list);
group = results[];
path = results[];
//临时存储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 = ;
string strReadline = string.Empty;
//读取group、path
while ((strReadline = sr.ReadLine()) != null)
{
if (i == )
group = strReadline;
else if (i == )
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 = ; long speed = * ;
long packSize = * ;
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 - ) - contentInfo.From) + ;
while (length > && packSize > )
{
byte[] fdfs = client.download_file(group_name, remote_filename, contentInfo.From, Math.Min(length, packSize));
outputStream.Write(fdfs, , fdfs.Length);
contentInfo.From = contentInfo.From + fdfs.Length;
length -= fdfs.Length;
if (sleep > ) 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

 

 

C#集成FastDFS断点续传的更多相关文章

  1. (转)Spring Boot(十八):使用 Spring Boot 集成 FastDFS

    http://www.ityouknow.com/springboot/2018/01/16/spring-boot-fastdfs.html 上篇文章介绍了如何使用 Spring Boot 上传文件 ...

  2. SpringBoot2.0集成FastDFS

    SpringBoot2.0集成FastDFS 前两篇整体上介绍了通过 Nginx 和 FastDFS 的整合来实现文件服务器.但是,在实际开发中对图片或文件的操作都是通过应用程序来完成的,因此,本篇将 ...

  3. Spring Boot(十八):使用 Spring Boot 集成 FastDFS

    上篇文章介绍了如何使用 Spring Boot 上传文件,这篇文章我们介绍如何使用 Spring Boot 将文件上传到分布式文件系统 FastDFS 中. 这个项目会在上一个项目的基础上进行构建. ...

  4. Spring Boot(十八):使用Spring Boot集成FastDFS

    Spring Boot(十八):使用Spring Boot集成FastDFS 环境:Spring Boot最新版本1.5.9.jdk使用1.8.tomcat8.0 功能:使用Spring Boot将文 ...

  5. spring boot(十八)集成FastDFS文件上传下载

    上篇文章介绍了如何使用Spring Boot上传文件,这篇文章我们介绍如何使用Spring Boot将文件上传到分布式文件系统FastDFS中. 这个项目会在上一个项目的基础上进行构建. 1.pom包 ...

  6. 使用Spring Boot集成FastDFS

    原文:http://www.cnblogs.com/ityouknow/p/8298358.html#3893468 上篇文章介绍了如何使用Spring Boot上传文件,这篇文章我们介绍如何使用Sp ...

  7. SpringBoot集成FastDFS依赖实现文件上传

    前言 对FastDFS文件系统安装后的使用. FastDFS的安装请参考这篇:Docker中搭建FastDFS文件系统(多图) 本文环境:IDEA + JDK1.8 + Maven 本文项目代码:ht ...

  8. spring boot集成FastDFS

    官方文档:https://github.com/happyfish100/fastdfs-client-java 一.首先,maven工程添加依赖 <!--fastdfs--> <d ...

  9. SpringBoot集成FastDFS+Nginx整合基于Token的防盗链

    为什么要用SpringBoot? SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人 ...

随机推荐

  1. cyclone IV中DDR的一个报错{Too many output and bidirectional pins per VCCIO and ground pair in I/O bank 8 }

    Error (169224): Too many output and bidirectional pins per VCCIO and ground pair in I/O bank 8 when ...

  2. WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析

    WebGIS实现在线要素编辑之ArcGIS Server 发布Feature Service 过程解析 FeatureService也称要素服务,其最大的好处就是支持在线要素编辑,并将编辑同步更新到后 ...

  3. 笔记 Bioinformatics Algorithms Chapter1

    Chapter1 WHERE IN THE GENOME DOES DNA REPLICATION BEGIN    一. ·聚合酶启动结构域会结合上游序列的一些位点,这些位点有多个,且特异,并且分布 ...

  4. (转)忘记wamp-mysql数据库root用户密码重置方法

    转自:http://www.jb51.net/article/28883.htm 1.打开任务管理器,结束进程  mysqld-nt.exe . 2.运行命令窗口 1)进行php服务管理器安装目录中的 ...

  5. POJ3723--Conscription(MST)WRONG

    Description Windy has a country, and he wants to build an army to protect his country. He has picked ...

  6. android 数据库更新

    SQLiteOpenHelper封装       继承SQLiteOpenHelper类,在构造方法中分别需要传入Context,数据库名称,CursorFactory(一般传入null,为默认数据库 ...

  7. Hdu2068 RPG的错排 2017-06-27 15:27 30人阅读 评论(0) 收藏

    RPG的错排 Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submis ...

  8. 关于FPGA的一些小见解

    Xilinx FPGA配置bit流文件 Xilinx FPGA的供电是采用USB作为电源,使用Verilog HDL或VHDL实现的逻辑电路通过Xilinx的综合工具生成bit流文件,通过Digile ...

  9. codeforces 480D

    题意:给定一些物品放置在承重为S的桌子上, 每个物品有5个属性,放置时间in,拿开时间out,重量w,承重s及放置后的收益v.而且后面放置上去的必须先拿开..求一种合法的放置使得收益最大,输出收益. ...

  10. UC浏览器 - 不负责任思考

    前言 UC浏览器的辉煌应该是我读大学(2008年)的时候,转眼间,十年过去了,庆幸的是UC还在,我从使用者变成了一名UC的员工. 以下都是个人的不负责任的猜想或者思考 变更 塞班时代 UC浏览器的地位 ...