Asp.Net 大文件下载

public class FileDownload : IDisposable
     readonly static string MULTIPART_BOUNDARY = "<q1w2e3r4t5y6u7i8o9p0>";
        readonly static string MULTIPART_CONTENTTYPE = "multipart/byteranges; boundary=" + MULTIPART_BOUNDARY;
        readonly static string HTTP_HEADER_Content_Disposition = "Content-Disposition";
        readonly static string HTTP_HEADER_ACCEPT_RANGES = "Accept-Ranges";
        readonly static string HTTP_HEADER_ACCEPT_RANGES_BYTES = "bytes";
        readonly static string HTTP_HEADER_CONTENT_TYPE = "Content-Type";
        readonly static string HTTP_HEADER_CONTENT_RANGE = "Content-Range";
        readonly static string HTTP_HEADER_CONTENT_LENGTH = "Content-Length";
        readonly static string HTTP_HEADER_ENTITY_TAG = "ETag";
        readonly static string HTTP_HEADER_LAST_MODIFIED = "Last-Modified";
        readonly static string HTTP_HEADER_RANGE = "Range";
        readonly static string HTTP_HEADER_IF_RANGE = "If-Range";
        readonly static string HTTP_HEADER_IF_MATCH = "If-Match";
        readonly static string HTTP_HEADER_IF_NONE_MATCH = "If-None-Match";
        readonly static string HTTP_HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
        readonly static string HTTP_HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
        readonly static string HTTP_HEADER_UNLESS_MODIFIED_SINCE = "Unless-Modified-Since";
        readonly static string HTTP_METHOD_GET = "GET";
        readonly static string HTTP_METHOD_HEAD = "HEAD";
        readonly static string HTTP_METHOD_POST = "POST";
        private string contentType = "application/octet-stream";

        public string ContentType
        {
            get { return contentType; }
            set { contentType = value; }
        }
Process Download
 /// <summary>
        
///  start to download file 
        
/// </summary>
        
/// <param name="fileName">download file full path</param>
        
/// <param name="headerFileName">as save file name</param>
        
/// <returns></returns>
        public DownLoadState ProcessDownload(string fileName, string headerFileName)
        {
            HttpContext objContext = HttpContext.Current;
            // The Response object from the Context 
            HttpResponse objResponse = objContext.Response;
            // The Request object from the Context 
            HttpRequest objRequest = objContext.Request;

            // File information object... 
            FileInformation fileInfo; 
            objResponse.BufferOutput = false;
 
            // Begin() contains start positions for each requested Range 
            long[] alRequestedRangesBegin = new long[1];
            // End() contains end positions for each requested Range 
            long[] alRequestedRangesend = new long[1];
            // Response Header value: Content Length... 
            int iResponseContentLength = 0;

            // The Stream we're using to download the file in chunks... 
            System.IO.Stream objStream;
            // Total Bytes to read (per requested range) 
            int iBytesToRead;
            // Size of the Buffer for chunk-wise reading 
            int iBufferSize = 25000;
            // The Buffer itself 
            byte[] bBuffer = new byte[iBufferSize];
            // Indicates if the download was interrupted 
            bool bDownloadBroken = false;
            // Indicates if this is a range request  
            bool bIsRangeRequest = false;
            // Indicates if this is a multipart range request 
            bool bMultipart = false;
            // Loop counter used to iterate through the ranges 
            int iLoop;
            if (string.IsNullOrEmpty(headerFileName))
                headerFileName = Path.GetFileName(fileName).Replace(' ''-');

            // Content-Disposition value 
            string Content_Disposition_File = "attachment; filename=" + headerFileName + "";
            fileInfo = new FileInformation(fileName);
            fileInfo.ContentType = this.ContentType;
            // Clear the current output content from the buffer 
            objResponse.Clear();

            if (!(objRequest.HttpMethod.Equals(HTTP_METHOD_GET) || objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD) || objRequest.HttpMethod.Equals(HTTP_METHOD_POST, StringComparison.OrdinalIgnoreCase)))
                // Currently, only the GET and HEAD methods are supported... 
                objResponse.StatusCode = 501;  // Not implemented 
            else if (!fileInfo.Exists)
                // The requested file could not be retrieved... 
                objResponse.StatusCode = 404;  // Not found 
            else if (fileInfo.Length > Int32.MaxValue)
                // The file size is too large...  
                objResponse.StatusCode = 413;  // Request Entity Too Large 
            else if (!ParseRequestHeaderRange(objRequest, ref alRequestedRangesBegin, ref alRequestedRangesend, fileInfo.Length, ref bIsRangeRequest))
                // The Range request contained bad entries 
                objResponse.StatusCode = 400;  // Bad Request 
            else if (!CheckIfModifiedSince(objRequest, fileInfo))
                // The entity is still unmodified... 
                objResponse.StatusCode = 304;  // Not Modified 
            else if (!CheckIfUnmodifiedSince(objRequest, fileInfo))
                // The entity was modified since the requested date...  
                objResponse.StatusCode = 412;  // Precondition failed 
            else if (!CheckIfMatch(objRequest, fileInfo))
                // The entity does not match the request...  
                objResponse.StatusCode = 412;  // Precondition failed 
            else if (!CheckIfNoneMatch(objRequest, objResponse, fileInfo))
            {
                // The entity does match the none-match request, the response  
                
// code was set inside the CheckifNoneMatch function 
            }
            else if (!ValidateDomain(objContext))
                // To check the url is from validate domain
                objResponse.StatusCode = 400;
            else
            {
                // Preliminary checks where successful...  
                if (bIsRangeRequest && CheckIfRange(objRequest, fileInfo))
                {
                    // This is a Range request...  
                    bMultipart = (alRequestedRangesBegin.GetUpperBound(0) > 0);

                    // Loop through each Range to calculate the entire Response length 
                    for (iLoop = alRequestedRangesBegin.GetLowerBound(0); iLoop <= alRequestedRangesBegin.GetUpperBound(0); iLoop++)
                    {
                        // The length of the content (for this range) 
                        iResponseContentLength += Convert.ToInt32(alRequestedRangesend[iLoop] - alRequestedRangesBegin[iLoop]) + 1;

                        if (bMultipart)
                        {
                            iResponseContentLength += HTTP_HEADER_Content_Disposition.Length;
                            iResponseContentLength += MULTIPART_BOUNDARY.Length;
                            iResponseContentLength += fileInfo.ContentType.Length;
                            iResponseContentLength += alRequestedRangesBegin[iLoop].ToString().Length;
                            iResponseContentLength += alRequestedRangesend[iLoop].ToString().Length;
                            iResponseContentLength += fileInfo.Length.ToString().Length;
                            iResponseContentLength += 49;
                        }
                    }

                    if (bMultipart)
                    {
                        iResponseContentLength += MULTIPART_BOUNDARY.Length;
                        // 8 is the length of dash and line break characters 
                        iResponseContentLength += 8;
                    }
                    else
                    {
                        // in the initial HTTP Header  
                        objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE, "bytes " +
                                alRequestedRangesBegin[0].ToString() + "-" +
                                alRequestedRangesend[0].ToString() + "/" +
                                fileInfo.Length.ToString());
                    }

                    // Range response  
                    objResponse.StatusCode = 206// Partial Response 
                }
                else
                {
                    // This is not a Range request, or the requested Range entity ID 
                    
// does not match the current entity ID, so start a new download 
                    
// Indicate the file//s complete size as content length 
                    iResponseContentLength = Convert.ToInt32(fileInfo.Length);
                    // Return a normal OK status... 
                    objResponse.StatusCode = 200;
                }

                // Write file name into the Response 
                objResponse.AppendHeader(HTTP_HEADER_Content_Disposition, Content_Disposition_File);
                // Write the content length into the Response 
                objResponse.AppendHeader(HTTP_HEADER_CONTENT_LENGTH, iResponseContentLength.ToString());
                // Write the Last-Modified Date into the Response 
                objResponse.AppendHeader(HTTP_HEADER_LAST_MODIFIED, fileInfo.LastWriteTimeUTC.ToString("r"));
                // Tell the client software that we accept Range request 
                objResponse.AppendHeader(HTTP_HEADER_ACCEPT_RANGES, HTTP_HEADER_ACCEPT_RANGES_BYTES);
                // Write the file//s Entity Tag into the Response (in quotes!) 
                objResponse.AppendHeader(HTTP_HEADER_ENTITY_TAG, "\"" + fileInfo.EntityTag + "\"");

                // Write the Content Type into the Response 
                if (bMultipart)
                    // Multipart messages have this special Type. 
                    
// In this case, the file//s actual mime type is 
                    
// written into the Response at a later time... 
                    objResponse.ContentType = MULTIPART_CONTENTTYPE;
                else
                    // Single part messages have the files content type... 
                    objResponse.ContentType = fileInfo.ContentType;
                if (objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD))
                {
                    // Only the HEAD was requested, so we can quit the Response right here...  
                }
                else
                {
                    // Flush the HEAD information to the client... 
                    objResponse.Flush();

                    // Download is in progress... 
                    fileInfo.State = DownLoadState.Progressing;

                    // Open the file as filestream 
                    objStream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
                    try
                    {
                        // Now, for each requested range, stream the chunks to the client: 
                        for (iLoop = alRequestedRangesBegin.GetLowerBound(0); iLoop <= alRequestedRangesBegin.GetUpperBound(0); iLoop++)
                        {
                            // Move the stream to the desired start position... 
                            objStream.Seek(alRequestedRangesBegin[iLoop], SeekOrigin.Begin);
                            // Calculate the total amount of bytes for this range 
                            iBytesToRead = Convert.ToInt32(alRequestedRangesend[iLoop] - alRequestedRangesBegin[iLoop]) + 1;

                            if (bMultipart)
                            {
                                // if this is a multipart response, we must add  
                                
// certain headers before streaming the content: 
                                
// The multipart boundary 
                                objResponse.Output.WriteLine("--" + MULTIPART_BOUNDARY);
                                // The mime type of this part of the content  
                                objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_TYPE + "" + fileInfo.ContentType);

                                // The actual range 
                                objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_RANGE + ": bytes " + alRequestedRangesBegin[iLoop].ToString() + "-" + alRequestedRangesend[iLoop].ToString() + "/" + fileInfo.Length.ToString());

                                /*objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE,": bytes " + 
                                        alRequestedRangesBegin[iLoop].ToString() + "-" + 
                                        alRequestedRangesend[iLoop].ToString() + "/" + 
                                        objFile.Length.ToString()); 
                                
*/
                                // Indicating the end of the intermediate headers 
                                objResponse.Output.WriteLine();

                            }

                            if (iBytesToRead > 0)
                            {
                                // Acording to Microsoft hotfix for .NET framework 2 the TransmitFile method got one overload 
                                
// If hotfix for .NET framework 2 installed 
                                objResponse.TransmitFile(fileName, alRequestedRangesBegin[iLoop], iBytesToRead);
                                // If hotfix not installed 
                                
//CallHiddenTransmitFile(objResponse, fileName, alRequestedRangesBegin[iLoop], iBytesToRead); 
                            }

                            // In Multipart responses, mark the end of the part  
                            if (bMultipart) objResponse.Output.WriteLine();
                            // No need to proceed to the next part if the  
                            
// client was disconnected 
                            if (bDownloadBroken) break;
                        }

                        // At this point, the response was finished or cancelled...  
                        if (bDownloadBroken)
                            // Download is broken... 
                            fileInfo.State = DownLoadState.Broken;
                        else
                        {
                            if (bMultipart)
                            {
                                // In multipart responses, close the response once more with  
                                
// the boundary and line breaks 
                                objResponse.Output.WriteLine("--" + MULTIPART_BOUNDARY + "--");
                                objResponse.Output.WriteLine();
                            }

                            // The download was finished 
                            fileInfo.State = DownLoadState.Finished;
                        }
                    }
                    finally
                    {
                        objStream.Close();
                    }
                }
            }

            return fileInfo.State;
        }
 
 private string[] domains = null;
        public string[] Domains
        {
            get
            {
                if (null == domains)
                {
                    string domainStr = ConfigurationManager.AppSettings["Domains"].ToLower();
                    domains = domainStr.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                }
                return domains;
            }
        }

        bool ValidateDomain(HttpContext context)
        {
            bool isValidate = true;
            // Check UrlReferrer is null
            if (null == context.Request.UrlReferrer || null == context.Request.UrlReferrer.Host)
            {
                isValidate = false;
            }
            else
            {
                // check the request url is from the domain
                if (!Domains.Contains(context.Request.UrlReferrer.Host.ToLower()))
                {
                    isValidate = false;
                }
            }
            return isValidate;
        }

        void CallHiddenTransmitFile(HttpResponse objResponse, string fileName, long offset, long size)
        {
            Type tempType = objResponse.GetType();
            FieldInfo tempInfo = tempType.GetField("_httpWriter", BindingFlags.NonPublic | BindingFlags.Instance);
            HttpWriter _httpWriter = (HttpWriter)tempInfo.GetValue(objResponse);

            tempType = tempInfo.FieldType;
            tempInfo = tempType.GetField("_buffers", BindingFlags.NonPublic | BindingFlags.Instance);
            ArrayList _buffers = (ArrayList)tempInfo.GetValue(_httpWriter);

            tempType = _httpWriter.GetType();
            tempInfo = tempType.GetField("_responseBufferingOn", BindingFlags.NonPublic | BindingFlags.Instance);
            bool responseBufferingOn = (bool)tempInfo.GetValue(_httpWriter);

            Type classType = tempType.Assembly.GetType("System.Web.HttpFileResponseElement");
            ConstructorInfo[] cinf = classType.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
            object ret = cinf[2].Invoke(new object[] { fileName, offset, size, truefalsetrue });
            _buffers.Add(ret);

            if (!responseBufferingOn)
            {
                objResponse.Flush();
            }
        }

        bool CheckIfRange(HttpRequest objRequest, FileInformation objFile)
        {
            string sRequestHeaderIfRange;

            // Retrieve If-Range Header value from Request (objFile.EntityTag if none is indicated) 
            sRequestHeaderIfRange = RetrieveHeader(objRequest, HTTP_HEADER_IF_RANGE, objFile.EntityTag);

            // If the requested file entity matches the current 
            
// file entity, return true 
            return sRequestHeaderIfRange.Equals(objFile.EntityTag);
        }

        bool CheckIfMatch(HttpRequest objRequest, FileInformation objFile)
        {
            string sRequestHeaderIfMatch;
            string[] sEntityIDs;
            bool breturn = false;

            // Retrieve If-Match Header value from Request (*, meaning any, if none is indicated) 
            sRequestHeaderIfMatch = RetrieveHeader(objRequest, HTTP_HEADER_IF_MATCH, "*");

            if (sRequestHeaderIfMatch.Equals("*"))
                // The server may perform the request as if the 
                
// If-Match header does not exists... 
                breturn = true;
            else
            {
                // One or more Match IDs where sent by the client software... 
                sEntityIDs = sRequestHeaderIfMatch.Replace("bytes=""").Split(",".ToCharArray());
                // Loop through all entity IDs, finding one  
                
// which matches the current file's ID will 
                
// be enough to satisfy the If-Match 
                for (int iLoop = sEntityIDs.GetLowerBound(0); iLoop <= sEntityIDs.GetUpperBound(0); iLoop++)
                {
                    if (sEntityIDs[iLoop].Trim().Equals(objFile.EntityTag))
                        breturn = true;
                }
            }

            return breturn;
        }

        bool CheckIfNoneMatch(HttpRequest objRequest, HttpResponse objResponse, FileInformation objFile)
        {
            string sRequestHeaderIfNoneMatch;
            string[] sEntityIDs;
            bool breturn = true;
            string sreturn = "";

            // Retrieve If-None-Match Header value from Request (*, meaning any, if none is indicated) 
            sRequestHeaderIfNoneMatch = RetrieveHeader(objRequest, HTTP_HEADER_IF_NONE_MATCH, String.Empty);

            if (sRequestHeaderIfNoneMatch.Equals(String.Empty))
                // Perform the request normally... 
                breturn = true;
            else
            {
                if (sRequestHeaderIfNoneMatch.Equals("*"))
                {
                    // The server must not perform the request  
                    objResponse.StatusCode = 412;  // Precondition failed 
                    breturn = false;
                }
                else
                {
                    // One or more Match IDs where sent by the client software... 
                    sEntityIDs = sRequestHeaderIfNoneMatch.Replace("bytes=""").Split(",".ToCharArray());
                    // Loop through all entity IDs, finding one which  
                    
// does not match the current file//s ID will be 
                    
// enough to satisfy the If-None-Match 
                    for (int iLoop = sEntityIDs.GetLowerBound(0); iLoop <= sEntityIDs.GetUpperBound(0); iLoop++)
                    {
                        if (sEntityIDs[iLoop].Trim().Equals(objFile.EntityTag))
                        {
                            sreturn = sEntityIDs[iLoop];
                            breturn = false;
                        }
                    }

                    if (!breturn)
                    {
                        // One of the requested entities matches the current file//s tag, 
                        objResponse.AppendHeader("ETag", sreturn);
                        objResponse.StatusCode = 304// Not Modified 
                    }
                }
            }

            return breturn;
        }

        bool CheckIfModifiedSince(HttpRequest objRequest, FileInformation objFile)
        {
            string sDate;
            DateTime dDate;
            bool breturn;

            // Retrieve If-Modified-Since Header value from Request (Empty if none is indicated) 
            sDate = RetrieveHeader(objRequest, HTTP_HEADER_IF_MODIFIED_SINCE, string.Empty);

            if (sDate.Equals(String.Empty))
                // No If-Modified-Since date was indicated,  
                
// so just give this as true  
                breturn = true;
            else
            {
                try
                {
                    // to parse the indicated sDate to a datetime value 
                    dDate = DateTime.Parse(sDate);
                    // return true if the file was modified since or at the indicated date... 
                    breturn = (objFile.LastWriteTimeUTC >= DateTime.Parse(sDate));
                }
                catch
                {
                    // Converting the indicated date value failed, return false  
                    breturn = false;
                }
            }
            return breturn;
        }

        bool CheckIfUnmodifiedSince(HttpRequest objRequest, FileInformation objFile)
        {
            string sDate;
            DateTime dDate;
            bool breturn;

            // Retrieve If-Unmodified-Since Header value from Request (Empty if none is indicated) 
            sDate = RetrieveHeader(objRequest, HTTP_HEADER_IF_UNMODIFIED_SINCE, String.Empty);

            if (sDate.Equals(String.Empty))
                // If-Unmodified-Since was not sent, check Unless-Modified-Since...  
                sDate = RetrieveHeader(objRequest, HTTP_HEADER_UNLESS_MODIFIED_SINCE, String.Empty);

            if (sDate.Equals(String.Empty))
                // No date was indicated,  
                
// so just give this as true  
                breturn = true;
            else
            {
                try
                {
                    //  to parse the indicated sDate to a datetime value 
                    dDate = DateTime.Parse(sDate);
                    // return true if the file was not modified since the indicated date... 
                    breturn = objFile.LastWriteTimeUTC < DateTime.Parse(sDate);
                }
                catch
                {
                    // Converting the indicated date value failed, return false  
                    breturn = false;
                }
            }
            return breturn;
        }

        bool ParseRequestHeaderRange(HttpRequest objRequest, ref long[] lBegin, ref long[] lEnd, long lMax, ref bool bRangeRequest)
        {
            bool bValidRanges;
            string sSource;
            int iLoop;
            string[] sRanges;

            // Retrieve Range Header value from Request (Empty if none is indicated) 
            sSource = RetrieveHeader(objRequest, HTTP_HEADER_RANGE, String.Empty);

            if (sSource.Equals(String.Empty))
            {
                // No Range was requested, return the entire file range... 
                lBegin = new long[1];
                //ReDim lBegin(0); 
                lEnd = new long[1];
                //ReDim lEnd(0); 
                lBegin[0] = 0;
                lEnd[0] = lMax - 1;
                // A valid range is returned 
                bValidRanges = true;
                // no Range request 
                bRangeRequest = false;
            }
            else
            {
                // A Range was requested...  
                bValidRanges = true;

                // return true for the bRange parameter, telling the caller 
                
// that the Request is indeed a Range request... 
                bRangeRequest = true;

                // Remove "bytes=" from the beginning, and split the remaining  
                
// string by comma characters 
                sRanges = sSource.Replace("bytes=""").Split(",".ToCharArray());
                lBegin = new long[sRanges.GetUpperBound(0) + 1];
                //ReDim lBegin(sRanges.GetUpperBound(0)); 
                lEnd = new long[sRanges.GetUpperBound(0) + 1];
                //ReDim lEnd(sRanges.GetUpperBound(0)); 

                
// Check each found Range request for consistency 
                for (iLoop = sRanges.GetLowerBound(0); iLoop <= sRanges.GetUpperBound(0); iLoop++)
                {
                    // Split this range request by the dash character,  
                    
// sRange(0) contains the requested begin-value, 
                    
// sRange(1) contains the requested end-value... 
                    string[] sRange = sRanges[iLoop].Split("-".ToCharArray());

                    // Determine the end of the requested range 
                    if (sRange[1].Equals(String.Empty))
                        // No end was specified, take the entire range 
                        lEnd[iLoop] = lMax - 1;
                    else
                        // An end was specified... 
                        lEnd[iLoop] = long.Parse(sRange[1]);

                    // Determine the begin of the requested range 
                    if (sRange[0].Equals(String.Empty))
                    {
                        // No begin was specified, which means that 
                        
// the end value indicated to return the last n 
                        
// bytes of the file: 

                        
// Calculate the begin 
                        lBegin[iLoop] = lMax - 1 - lEnd[iLoop];
                        // ... to the end of the file... 
                        lEnd[iLoop] = lMax - 1;
                    }
                    else
                        // A normal begin value was indicated... 
                        lBegin[iLoop] = long.Parse(sRange[0]);

                    // Begin and end must not exceed the file size 
                    if ((lBegin[iLoop] > (lMax - 1)) || (lEnd[iLoop] > (lMax - 1)))
                        bValidRanges = false;

                    // Begin and end cannot be < 0 
                    if ((lBegin[iLoop] < 0) || (lEnd[iLoop] < 0))
                        bValidRanges = false;

                    // End must be larger or equal to begin value 
                    if (lEnd[iLoop] < lBegin[iLoop])
                        // The requested Range is invalid... 
                        bValidRanges = false;
                }

            }
            return bValidRanges;
        }

        string RetrieveHeader(HttpRequest objRequest, string sHeader, string sDefault)
        {
            string sreturn;

            // Retrieves the indicated Header//s value from the Request, 
            
// if the header was not sent, sDefault is returned. 
            
// If the value contains quote characters, they are removed. 
            sreturn = objRequest.Headers[sHeader];

            if ((sreturn == null) || (sreturn.Equals(string.Empty)))
                // The Header wos not found in the Request,  
                
// return the indicated default value... 
                return sDefault;
            else
                // return the found header value, stripped of any quote characters... 
                return sreturn.Replace("\"""");
        }

        string GenerateHash(System.IO.Stream objStream, long lBegin, long lEnd)
        {
            byte[] bByte = new byte[Convert.ToInt32(lEnd)];

            objStream.Read(bByte, Convert.ToInt32(lBegin), Convert.ToInt32(lEnd - lBegin) + 1);

            //Instantiate an MD5 Provider object 
            MD5CryptoServiceProvider Md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();

            //Compute the hash value from the source 
            byte[] ByteHash = Md5.ComputeHash(bByte);

            //And convert it to String format for return 
            return Convert.ToBase64String(ByteHash);
        }

        #region IDisposable Members

        void IDisposable.Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion
File Information
 1 public class FileInformation
 2     {
 3         private DownLoadState state;
 4         private DateTime createTime;
 5         private string fileName = "";
 6         private string contentType = "application/octet-stream";
 7         private long? fileLength = null;
 8 
 9         public FileInformation(string fileName)
10         {
11             this.fileName = fileName;
12             createTime = DateTime.Now;
13         }
14 
15         public bool Exists
16         {
17             get
18             {
19                 return File.Exists(fileName);
20             }
21         }
22 
23         public string FullName
24         {
25             get { return fileName;}
26         }
27 
28         public DateTime LastWriteTimeUTC
29         {
30             get { return createTime.ToUniversalTime(); }
31         }
32 
33         public long Length
34         {
35             get
36             {
37                 if (fileLength.HasValue == false)
38                 {
39                     FileInfo info = new FileInfo(fileName);
40                     fileLength = info.Length;
41                 }
42                 return fileLength.Value;
43             }
44         }
45 
46         public string ContentType
47         {
48             get
49             {
50                 return contentType;
51             }
52             set
53             {
54                 contentType = value;
55             }
56         }
57         public string EntityTag
58         {
59             get
60             {
61                 return fileName.GetHashCode().ToString();; 
62             }
63         }
64 
65         public virtual DownLoadState State
66         {
67             get
68             {
69                 return state;
70             }
71             set
72             {
73                 state = value;
74             }
75         }
76     }
 
DownLoad Status 
 1  public enum DownLoadState
 2     {
 3         /// Clear: No download in progress,  
 4         /// the file can be manipulated 
 5         Clear = 1
 6 
 7         /// Locked: A dynamically created file must 
 8         /// not be changed 
 9         Locked = 2
10 
11         /// In Progress: File is locked, and download  
12         /// is currently in progress 
13         Progressing = 6
14 
15         /// Broken: File is locked, download was in 
16         /// progress, but was cancelled  
17         Broken = 10
18 
19         /// Finished: File is locked, download 
20         /// was completed 
21         Finished = 18
22     }
原文地址:https://www.cnblogs.com/OSoft/p/2282347.html