[fix]Config创建默认配置文件的开关Runtime.CreateConfigOnMissing,仅需对自动创建生效,而不应该阻止用户主动Save
智能大石头 authored at 2024-08-09 00:30:41 石头 committed at 2024-08-10 14:22:24
5.62 KiB
X
using System;
using System.IO;
using System.Web;
using System.Web.Caching;
using NewLife.Log;
using NewLife.Web;

namespace NewLife.CommonEntity.Web
{
    /// <summary>附件处理器</summary>
    public class AttachmentHttpHandler : IHttpHandler
    {
        #region IHttpHandler 成员
        bool IHttpHandler.IsReusable { get { return false; } }

        void IHttpHandler.ProcessRequest(HttpContext context) { OnProcess(context); }
        #endregion

        #region 属性
        /// <summary>文件编号</summary>
        public Int32 ID { get { return HttpContext.Current.Request["ID"].ToInt(); } }

        private Boolean _BrowserCache;
        /// <summary>是否使用浏览器缓存 默认禁用</summary>
        public Boolean BrowserCache { get { return _BrowserCache; } set { _BrowserCache = value; } }

        private TimeSpan _browserCacheMaxAge = new TimeSpan(100, 0, 0, 0);
        /// <summary>浏览器最大缓存时间 默认100天</summary>
        public TimeSpan BrowserCacheMaxAge { get { return _browserCacheMaxAge; } set { _browserCacheMaxAge = value; } }
        #endregion

        #region 文件流
        /// <summary>获取文件数据流。这里可以实现小文件缓存进入内容以减少磁盘IO</summary>
        /// <param name="context"></param>
        /// <param name="attachment"></param>
        /// <returns></returns>
        protected virtual Stream GetStream(HttpContext context, IAttachment attachment)
        {
            if (attachment.Size > MaxFileSize) return new FileStream(attachment.FullFilePath, FileMode.Open, FileAccess.Read);

            var key = "NewLife.Attachment.Cache_" + attachment.ID;
            var stream = HttpRuntime.Cache[key] as Stream;
            if (stream != null) return stream;

            var fs = new FileStream(attachment.FullFilePath, FileMode.Open, FileAccess.Read);
            // 再次判断,防止记录的文件大小不正确而导致系统占用大量内存
            if (fs.Length > MaxFileSize) return fs;

            var buffer = new Byte[fs.Length];
            fs.Read(buffer, 0, buffer.Length);
            fs.Close();

            stream = new MemoryStream(buffer);
            HttpRuntime.Cache.Insert(key, stream, null, DateTime.Now.Add(CacheTime), Cache.NoSlidingExpiration);
            return stream;
        }

        /// <summary>小于指定大小才缓存文件</summary>
        protected virtual Int64 MaxFileSize { get { return 100 * 1024; } }

        /// <summary>小文件缓存时间,默认10分钟</summary>
        /// <returns></returns>
        protected virtual TimeSpan CacheTime { get { return new TimeSpan(0, 10, 0); } }
        #endregion

        #region 方法
        /// <summary>处理</summary>
        /// <param name="context"></param>
        protected virtual void OnProcess(HttpContext context)
        {
            var attachment = GetAttachment();

            if (attachment == null || !attachment.IsEnable || !File.Exists(attachment.FullFilePath))
            {
                OnNotFound(context);
                return;
            }

            // 增加统计
            attachment.Increment(null);

            // 检查浏览器缓存
            var wd = new WebDownload();
            wd.ModifyTime = attachment.UploadTime;
            if (wd.CheckCache()) return;

            var stream = GetStream(context, attachment);
            if (stream.CanSeek && stream.Position >= stream.Length) stream.Position = 0;
            try
            {
                OnResponse(context, attachment, stream, null);
            }
            catch (Exception ex)
            {
                XTrace.WriteException(ex);
            }
            finally
            {
                // 关闭文件流
                if (stream is FileStream) stream.Close();
            }
        }

        /// <summary>取得附件对象</summary>
        /// <returns></returns>
        protected virtual IAttachment GetAttachment()
        {
            if (ID > 0)
                return Attachment.FindByID(ID);
            else
                return null;
        }

        /// <summary>没找到附件</summary>
        /// <param name="context"></param>
        protected virtual void OnNotFound(HttpContext context)
        {
            HttpResponse Response = context.Response;

            Response.StatusCode = 400;
            //Response.Status = "文件未找到!";

        }

        /// <summary>响应</summary>
        /// <param name="context"></param>
        /// <param name="attachment"></param>
        /// <param name="stream"></param>
        /// <param name="dispositionMode"></param>
        protected virtual void OnResponse(HttpContext context, IAttachment attachment, Stream stream, String dispositionMode)
        {
            var wd = new WebDownload();
            wd.Stream = stream;
            wd.FileName = attachment.FileName;
            if (!String.IsNullOrEmpty(dispositionMode)) wd.Mode = WebDownload.ParseMode(dispositionMode);
            if (!String.IsNullOrEmpty(attachment.ContentType)) wd.ContentType = attachment.ContentType;

            if (BrowserCache)
            {
                wd.BrowserCache = true;
                wd.ModifyTime = attachment.UploadTime;
                wd.BrowserCacheMaxAge = BrowserCacheMaxAge;
            }

            OnReader(wd);
            wd.Render();
        }

        /// <summary>参数准备完毕,输出前</summary>
        /// <param name="wd"></param>
        protected virtual void OnReader(WebDownload wd) { }
        #endregion
    }
}