دانلود فایل با قابلیت Resume
پنجشنبه 15 مرداد 1394در این مقاله قصد داریم نحوه دانلود فایل با قابلیت دانلود ادامه فایل در زمان دیگری را بررسی کنیم .
این کار را میتوانید توسط کلاس زیر انجام دهید
using System; using System.IO; using System.Web; using System.Collections; using System.Data; using System.Net; using System.Security.Cryptography; namespace SoftProjects.WebPresentation.Utility { /// <summary> /// Orginal class name is "ZIPHandler" in VB.Net /// http://www.devx.com /// /// Optimization and convertion by SalarSoft /// http://salarsoft.somee.com /// /// Last update: 2008-2-12 /// </summary> public class ResumeDownload : IDisposable { const string MULTIPART_BOUNDARY = "<q1w2e3r4t5y6u7i8o9p0>"; const string MULTIPART_CONTENTTYPE = "multipart/byteranges; boundary=" + MULTIPART_BOUNDARY; //Response.AddHeader("Content-Disposition","attachment; filename=" + Path.GetFileName(fUrl)); const string HTTP_HEADER_Content_Disposition = "Content-Disposition"; const string HTTP_HEADER_ACCEPT_RANGES = "Accept-Ranges"; const string HTTP_HEADER_ACCEPT_RANGES_BYTES = "bytes"; const string HTTP_HEADER_CONTENT_TYPE = "Content-Type"; const string HTTP_HEADER_CONTENT_RANGE = "Content-Range"; const string HTTP_HEADER_CONTENT_LENGTH = "Content-Length"; const string HTTP_HEADER_ENTITY_TAG = "ETag"; const string HTTP_HEADER_LAST_MODIFIED = "Last-Modified"; const string HTTP_HEADER_RANGE = "Range"; const string HTTP_HEADER_IF_RANGE = "If-Range"; const string HTTP_HEADER_IF_MATCH = "If-Match"; const string HTTP_HEADER_IF_NONE_MATCH = "If-None-Match"; const string HTTP_HEADER_IF_MODIFIED_SINCE = "If-Modified-Since"; const string HTTP_HEADER_IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; const string HTTP_HEADER_UNLESS_MODIFIED_SINCE = "Unless-Modified-Since"; const string HTTP_METHOD_GET = "GET"; const string HTTP_METHOD_HEAD = "HEAD"; private string fContentType = "application/octet-stream"; public string ContentType { get { return fContentType; } set { fContentType = value; } } public void Dispose() { } public void ClearResponseData() { HttpResponse objResponse = HttpContext.Current.Response; objResponse.ClearContent(); objResponse.ClearHeaders(); } 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 objFile; // Long Arrays for Range values: // ...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]; // Amount of Bytes read int iLengthOfReadChunk = -1; // 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 + ""; // ToDo - your code here (Determine which file is requested) // Using objRequest, determine which file is requested to // be downloaded, and open objFile with that file: // Example: // objFile = New Download.FileInformation(<Full path to the requested file>) //objFile = new Download.FileInformation(objContext.Server.MapPath("~/download.zip")); objFile = new FileInformation(fileName); objFile.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))) // Currently, only the GET and HEAD methods // are supported... objResponse.StatusCode = 501; // Not implemented else if (!objFile.Exists) // The requested file could not be retrieved... objResponse.StatusCode = 404; // Not found else if (objFile.Length > Int32.MaxValue) // The file size is too large... objResponse.StatusCode = 413; // Request Entity Too Large else if (!ParseRequestHeaderRange(objRequest, ref alRequestedRangesBegin, ref alRequestedRangesend, objFile.Length, ref bIsRangeRequest)) // The Range request contained bad entries objResponse.StatusCode = 400; // Bad Request else if (!CheckIfModifiedSince(objRequest, objFile)) // The entity is still unmodified... objResponse.StatusCode = 304; // Not Modified else if (!CheckIfUnmodifiedSince(objRequest, objFile)) // The entity was modified since the requested date... objResponse.StatusCode = 412; // Precondition failed else if (!CheckIfMatch(objRequest, objFile)) // The entity does not match the request... objResponse.StatusCode = 412; // Precondition failed else if (!CheckIfNoneMatch(objRequest, objResponse, objFile)) { // The entity does match the none-match request, the response // code was set inside the CheckifNoneMatch function } else { // Preliminary checks where successful... if (bIsRangeRequest && CheckIfRange(objRequest, objFile)) { // This is a Range request... // if the Range arrays contain more than one entry, // it even is a multipart 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; // if this is a multipart range request, calculate // the length of the intermediate headers to send iResponseContentLength += MULTIPART_BOUNDARY.Length; iResponseContentLength += objFile.ContentType.Length; iResponseContentLength += alRequestedRangesBegin[iLoop].ToString().Length; iResponseContentLength += alRequestedRangesend[iLoop].ToString().Length; iResponseContentLength += objFile.Length.ToString().Length; // 49 is the length of line break and other // needed characters in one multipart header iResponseContentLength += 49; } } if (bMultipart) { // if this is a multipart range request, // we must also calculate the length of // the last intermediate header we must send iResponseContentLength += MULTIPART_BOUNDARY.Length; // 8 is the length of dash and line break characters iResponseContentLength += 8; } else { // This is no multipart range request, so // we must indicate the response Range of // in the initial HTTP Header objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE, "bytes " + alRequestedRangesBegin[0].ToString() + "-" + alRequestedRangesend[0].ToString() + "/" + objFile.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(objFile.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, objFile.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, "\"" + objFile.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 = objFile.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... objFile.State = DownloadState.fsDownloadInProgress; // Open the file as filestream objStream = new FileStream(objFile.FullName, FileMode.Open, FileAccess.Read, FileShare.Read); try { //objStream = objFile.DataStream; // 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); //objResponse.AppendHeader("--",MULTIPART_BOUNDARY); // The mime type of this part of the content objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_TYPE + ": " + objFile.ContentType); //objResponse.AppendHeader(HTTP_HEADER_CONTENT_TYPE,objFile.ContentType); // The actual range objResponse.Output.WriteLine(HTTP_HEADER_CONTENT_RANGE + ": bytes " + alRequestedRangesBegin[iLoop].ToString() + "-" + alRequestedRangesend[iLoop].ToString() + "/" + objFile.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(); } // Now stream the range to the client... while (iBytesToRead > 0) { if (objResponse.IsClientConnected) { // Read a chunk of bytes from the stream iLengthOfReadChunk = objStream.Read(bBuffer, 0, Math.Min(bBuffer.Length, iBytesToRead)); // Write the data to the current output stream. objResponse.OutputStream.Write(bBuffer, 0, iLengthOfReadChunk); // Flush the data to the HTML output. objResponse.Flush(); // Clear the buffer bBuffer = new byte[iBufferSize]; //ReDim bBuffer(iBufferSize); // Reduce BytesToRead iBytesToRead -= iLengthOfReadChunk; } else { // The client was or has disconneceted from the server... stop downstreaming... iBytesToRead = -1; bDownloadBroken = true; } } // 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... objFile.State = DownloadState.fsDownloadBroken; 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 objFile.State = DownloadState.fsDownloadFinished; } } finally { objStream.Close(); } } } //objResponse.End(); //====== return download state ====== return objFile.State; } bool CheckIfRange(HttpRequest objRequest, FileInformation objFile) { string sRequestHeaderIfRange; // Checks the If-Range header if it was sent with the request. // // returns true if the header value matches the file//s entity tag, // or if no header was sent, // returns false if a header was sent, but does not match the file. // 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; // Checks the If-Match header if it was sent with the request. // // returns true if one of the header values matches the file//s entity tag, // or if no header was sent, // returns false if a header was sent, but does not match the file. // 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 the result... return breturn; } bool CheckIfNoneMatch(HttpRequest objRequest, HttpResponse objResponse, FileInformation objFile) { string sRequestHeaderIfNoneMatch; string[] sEntityIDs; bool breturn = true; string sreturn = ""; // Checks the If-None-Match header if it was sent with the request. // // returns true if one of the header values matches the file//s entity tag, // or if "*" was sent, // returns false if a header was sent, but does not match the file, or // if no header was sent. // 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 the result... return breturn; } bool CheckIfModifiedSince(HttpRequest objRequest, FileInformation objFile) { string sDate; DateTime dDate; bool breturn; // Checks the If-Modified header if it was sent with the request. // // returns true, if the file was modified since the // indicated date (RFC 1123 format), or // if no header was sent, // returns false, if the file was not modified since // the indicated date // 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; // Checks the If-Unmodified or Unless-Modified-Since header, if // one of them was sent with the request. // // returns true, if the file was not modified since the // indicated date (RFC 1123 format), or // if no header was sent, // returns false, if the file was modified since the indicated date // 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; // Parses the Range header from the Request (if there is one) // returns true, if the Range header was valid, or if there was no // Range header at all (meaning that the whole // file was requested) // returns false, if the Range header asked for unsatisfieable // ranges // 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... // Preset value... 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]); // Check if the requested range values are valid, // return false if they are not. // // Note: // Do not clean invalid values up by fitting them into // valid parameters using Math.Min and Math.Max, because // some download clients (like Go!Zilla) might send invalid // (e.g. too large) range requests to determine the file limits! // 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); } } public class FileInformation { DownloadState m_nState; DateTime fCreatTime; string fFileName = ""; string m_ContentType = "application/octet-stream"; long? fileLength = null; public FileInformation(string fileName) { fFileName = fileName; fCreatTime = DateTime.Now; } public bool Exists { get { return File.Exists(fFileName); } } public string FullName { get { return fFileName;/*m_objFile.FullName;*/} } public DateTime LastWriteTimeUTC { get { return fCreatTime.ToUniversalTime(); } } public long Length { get { if (fileLength.HasValue == false) { FileInfo info = new FileInfo(fFileName); fileLength = info.Length; } return fileLength.Value; } } public string ContentType { get { return m_ContentType; } set { m_ContentType = value; } } public string EntityTag { get { return fFileName.GetHashCode().ToString();//"MyExampleFileID"; } } public virtual DownloadState State { get { return m_nState; } set { m_nState = value; } } } public enum DownloadState { /// Clear: No download in progress, /// the file can be manipulated fsClear = 1, /// Locked: A dynamically created file must /// not be changed fsLocked = 2, /// In Progress: File is locked, and download /// is currently in progress fsDownloadInProgress = 6, /// Broken: File is locked, download was in /// progress, but was cancelled fsDownloadBroken = 10, /// Finished: File is locked, download /// was completed fsDownloadFinished = 18 }; }
- ASP.net
- 1k بازدید
- 3 تشکر