.net版本FastDFS客戶端v5.05。html
https://github.com/zhouyh362329/fastdfs.client.netgit
FastDFS環境準備。github
http://www.cnblogs.com/ddrsql/p/7118695.htmlweb
webuploader。sql
http://fex.baidu.com/webuploader/app
非斷點續傳方式下載。測試
/// <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(); } }
下載完成前客戶端不知道文件大小。this
斷點續傳幾個關鍵Header屬性:spa
Request.net
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; }
斷點續傳方式下載文件效果:
文件分段上傳至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中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