// Copyright (C) 2002 Mark Pasternak
//
// This software is provided 'as-is', without any express or implied
// warranty.  In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
//    claim that you wrote the original software. If you use this software
//    in a product, an acknowledgment in the product documentation would be
//    appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
//    misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// Mark Pasternak (mark.pasternak@universum.se)


// µ¥ºêÇÇ¾Æ ASP.NET ÀÚ·á½Ç¿¡¼­ ÆÛ¿Â ÆÄÀÏ ´Ù¿î·Îµå Å¬·¹½ºÀÔ´Ï´Ù.
// ¿äÃ»ÇÏ½Å ºÐÀÌ °è¼Å¼­ °£´ÜÇÑ ÁÖ¼®À» ´Þ¾Ò½À´Ï´Ù.
//
// ÁÖ¼® Ã·ºÎ : Á¶¼ºÁø (http://powerof.net)


using System;
using System.Web;
using System.IO;

namespace MarkPasternak.Utility
{
	/// <summary>
	/// The HttpResponse.WriteFile method does not work very well with large files since it reads
	/// the whole file to memory at once. This may crash the ASP.NET worker process if the file is too big.
	/// This helper class uses buffers and continuously checks if the client is connected before it sends any output.
	///
	/// HttpResponse.WriteFile ¸Þ¼Òµå´Â ¾ÆÁÖ Å« ÆÄÀÏÀ» Àü¼ÛÇÏ´Â °æ¿ì Àß µ¿ÀÛÇÏÁö ¾Ê½À´Ï´Ù. 
	/// ¿Ö³Ä¸é ÀÌ ¸Þ¼Òµå´Â Àü¼ÛÇÒ ÆÄÀÏÀ» ¸Þ¸ð¸®·Î ÇÑ¹ø¿¡ ¸ðµÎ ÀÐ¾îµå¸®±â ¶§¹®¿¡ 
	/// Àü¼ÛÇÒ ÆÄÀÏÀÌ Å©´Ù¸é ¼­¹ö¿¡ ¸¹Àº ºÎ´ãÀ» ÁÝ´Ï´Ù. ½É°¢ÇÑ °æ¿ì´Â ASP.NET ÇÁ·Î¼¼½º°¡ Á×´Â °æ¿ìµµ »ý±âÁÒ.
	/// ´ÙÀ½ÀÇ Å¬·¹½º´Â ¹öÆÛ¸¦ ÀÌ¿ëÇØ ÆÄÀÏÀ» ÀÐ°í Àü¼ÛÇÏ±â ¶§¹®¿¡ 
	/// ±âÁ¸ HttpResponse.WriteFile ¸Þ¼ÒµåÀÇ ¹®Á¦¸¦ ÇØ°áÇØÁÝ´Ï´Ù.
	/// ±×¸®°í Å¬¶óÀÌ¾ðÆ®¿ÍÀÇ ¿¬°áÀ» °è¼Ó Á¡°ËÇÏ±â¶§¹®¿¡ Å¬¶óÀÌ¾ðÆ®¿¡¼­ ÆÄÀÏÀü¼ÛÀ» 
	/// Ãë¼ÒÇÏ°Å³ª ´Ù¸¥ ÀÌÀ¯·Î ¿¬°áÀÌ ²÷±æ°æ¿ì ´õÀÌ»ó ÆÄÀÏÀ» Àü¼ÛÇÏÁö ¾Ê½À´Ï´Ù.
	/// </summary>
	public class WriteFileHelper
	{
		private int m_bufferSize=4096; //¹öÆÛÅ©±â¸¦ 4¸Þ°¡·Î ÁöÁ¤
		private HttpContext Context;
		public EventHandler DownloadCancelled;
		public EventHandler DownloadCompleted;

		public WriteFileHelper()
		{
			Context = HttpContext.Current;
		}

		/// <summary>
		/// Sets and gets the size of the buffer that is used when a file is read to memory.
		/// A larger buffer will require more memory, but will on the other hand make 
		/// it less resource intensive to send a file. Experiment to find a good balance.
		/// </summary>
		/// À§¿¡¼­ m_bufferSize°¡ 4096À¸·Î ±âº»ÀûÀ¸·Î ¼³Á¤µÇÁö¸¸
		/// ¿øÇÑ´Ù¸é ÀÌ ¼Ó¼ºÀ» ÀÌ¿ëÇØ ¹öÆÛÅ©±â¸¦ º¯°æÇÒ¼ö ÀÖ´Ù.
		public int BufferSize
		{
			set{m_bufferSize=value;}
			get{return m_bufferSize;}
		}

		// »ç¿ëÀÚÀÚ ÆÄÀÏ Àü¼ÛÀ» Ãë¼ÒÇÏ°Å³ª ¾î¶² ÀÌÀ¯·Î Å¬¶óÀÌ¾ðÆ®¿Í ¿¬°áÀÌ ²ö±â¸é 
		// ÀÌ ¸Þ¼Òµå°¡ È£ÃâµË´Ï´Ù.
		// ÀÌ Å¬·¹½º ¿ÜºÎ¿¡¼­ DownloadCancelled ÀÇ ÀÌº¥Æ® Çîµé·¯¸¦ ÁöÁ¤ÇÏ¸é
		// ÀÌ ¸Þ¼Òµå°¡ È£ÃâµÉ¶§ ÀÓÀÇÀÇ Ã³¸®¸¦ ÇÒ¼ö ÀÖ½À´Ï´Ù.
		protected void OnDownloadCancelled() 
		{ 
			if (DownloadCancelled != null) // Çîµé·¯°¡ ÁöÁ¤µÇ ÀÖ´Ù¸é
				DownloadCancelled(null, null); 
		}

		// ÆÄÀÏÀü¼ÛÀÌ ¿Ï·áµÇ¸é ÀÌ ¸Þ¼Òµå°¡ È£ÃâµË´Ï´Ù.
		// ÀÌ Å¬·¹½º ¿ÜºÎ¿¡¼­ DownloadCompleted ÀÇ ÀÌº¥Æ® Çîµé·¯¸¦ ÁöÁ¤ÇÏ¸é
		// ÀÌ ¸Þ¼Òµå°¡ È£ÃâµÉ¶§ ÀÓÀÇÀÇ Ã³¸®¸¦ ÇÒ¼ö ÀÖ½À´Ï´Ù.
		protected void OnDownloadCompleted() 
		{ 
			if (DownloadCompleted != null) // Çîµé·¯°¡ ÁöÁ¤µÇ ÀÖ´Ù¸é
				DownloadCompleted(null, null); 
		}

		/// <summary>
		/// Writes a file to the Response Stream
		/// </summary>
		/// <param name="filePath">A Path to a file</param>
		/// Àü¼ÛÇÒ ÆÄÀÏÀÇ °æ·Î¸¦ ÀÎÀÚ·Î ÀÌ ¸Þ¼Òµå¸¦ È£ÃâÇÏ¸é 
		/// Å¬¶óÀÌ¾ðÆ®·Î ÆÄÀÏÀ» º¸³À´Ï´Ù.
		/// ±×·¯³ª ÀÌ ¸Þ¼Òµå´ë½Å ´ÙÀ½¿¡ ³ª¿À´Â 
		/// WriteFileToResponseStreamWithForceDownloadHeaders ¸Þ¼Òµå¸¦ »ç¿ëÇÏ´Â°Ô ´õ Æí¸®ÇÕ´Ï´Ù.
		/// ±×·± ÀÌÀ¯·Î ÀÌ ¸Þ¼Òµå´Â º°·Î »ç¿ëÇÒ ÀÏÀÌ ¾øÀ»µí ÇÏ±º¿ä
		public void WriteFileToResponseStream(string filePath)
		{
			if(!File.Exists(filePath)) // ¿äÃ»ÇÑ ÆÄÀÏÀÌ ¾øÀ¸¸é ¿¡·¯¸¦ ÀÏÀ¸Å²´Ù.
				throw new Exception("File Path does not exist : " + filePath);

			// Å¬¶óÀÌ¾ðÆ®·Î ÆÄÀÏÀ» Àü¼ÛÇÑ´Ù.
			WriteBinaryFile_Internal(filePath);
		}

		/// <summary>
		/// Writes a file to the Response Stream and adds the headers sÀåthe "Save As" dialog is presented to the user
		/// </summary>
		/// <param name="filePath">A Path to a file</param>
		/// Àü¼ÛÇÒ ÆÄÀÏÀÇ °æ·Î¸¦ ÀÎÀÚ·Î ÀÌ ¸Þ¼Òµå¸¦ È£ÃâÇÏ¸é 
		/// Å¬¶óÀÌ¾ðÆ®·Î HTTP Çì´õ¸¦ Ã·ºÎÇØ¼­ ÆÄÀÏÀ» Àü¼ÛÇÕ´Ï´Ù.
		/// Ã·ºÎÇÑ Çì´õ·Î ÀÎÇØ Å¬¶óÀÌ¾ðÆ®¿¡´Â '¿­±â','ÀúÀå', 'Ãë¼Ò' ¹öÆ°ÀÌ ÀÖ´Â 
		/// ´ëÈ­»óÀÚ°¡ ¿­¸³´Ï´Ù.
		public void WriteFileToResponseStreamWithForceDownloadHeaders(string filePath)
		{
			if(!File.Exists(filePath)) // ¿äÃ»ÇÑ ÆÄÀÏÀÌ ¾øÀ¸¸é ¿¡·¯¸¦ ÀÏÀ¸Å²´Ù.
				throw new Exception("File Path does not exist : " + filePath);

			Context.Response.Clear();
			// ÇØ´õ¸¦ Ãß°¡ÇÑ´Ù
			Context.Response.ContentType = "application/octet-stream";
			Context.Response.AddHeader("Content-Disposition", "attachment; filename=" + Path.GetFileName(filePath));
			Context.Response.AddHeader("Content-Length", new FileInfo(filePath).Length.ToString());

			// Å¬¶óÀÌ¾ðÆ®·Î ÆÄÀÏÀ» Àü¼ÛÇÑ´Ù.
			WriteBinaryFile_Internal(filePath);
		}

		// ½ÇÁúÀûÀÎ ÆÄÀÏÀü¼ÛÀ» ´ã´çÇÏ´Â ¸Þ¼Òµå
		private void WriteBinaryFile_Internal(string filePath)
		{
			Context.Response.Buffer=false; // ¹öÆÛ¸µÀ» ÇÏÁö ¾Ê´Â´Ù.
			FileStream inStr = null;
			byte[] buffer = new byte[m_bufferSize]; // ÆÄÀÏÀ» ÀÐ¾îµå¸± ¹öÆÛ
			long byteCount;
			try
			{
				inStr = File.OpenRead(filePath); // ÆÄÀÏÀ» ¿¬´Ù.
				// m_bufferSize Å©±â ¸¸Å­¾¿ ÆÄÀÏ ³»¿ëÀ» Â÷·Ê´ë·Î ÀÐ¾îµéÀÎ´Ù.
				while ((byteCount = inStr.Read(buffer, 0, buffer.Length)) > 0) 
				{
					// Å¬¶óÀÌ¾ðÆ®°¡ ¿¬°áµÇ ÀÖ´ÂÁö °Ë»çÇÑ´Ù.
					// Áï, »ç¿ëÀÚ°¡ ÆÄÀÏÀü¼ÛÀ» Áß°£¿¡ Ãë¼ÒÇÏ°Å³ª
					// ´Ù¸¥ ÀÌÀ¯·Î ¿¬°áÀÌ ²ö±â¸é IsClientConnected°¡ talse°¡ µÈ´Ù.
					if(Context.Response.IsClientConnected)
					{
						// ¹öÆÛ¿¡ ´ã±ä µ¥ÀÌÅÍ¸¦ Å¬¶óÀÌ¾ðÆ®·Î Àü¼ÛÇÑ´Ù.
						Context.Response.OutputStream.Write(buffer, 0, buffer.Length);
						Context.Response.Flush();
					}
					else 
					{
						// Àü¼ÛÀÌ Ãë¼Ò µÆÀ½À» ¾Ë¸®´Â ÀÌº¥Æ®¸¦ ¹ß»ý½ÃÅ²´Ù.
						OnDownloadCancelled();
						return;
					}
				}
				OnDownloadCompleted(); // Àü¼Û ¿Ï·á ÀÌº¥Æ®¸¦ ¹ß»ý½ÃÅ²´Ù.
			}
			catch(Exception ex)
			{
				throw ex;
			}
			finally
			{
				inStr.Close();
				Context.Response.End();
			}
		}


	}
}
