asp.net上传功能(单文件,多文件,自定义生成缩略图,水印)

前言

上传功能,是大家经常用到了,可能每一个项目都可以会用到。网上到处都有上传功能的代码。比我写的好的有很多。我这里也仅是分享我的代码。

 

功能实现点

1.单个文件上传;

2.多个文件上传;

3.对于图片等类型的图像,可以自定义生成缩略图大小;

4.文件服务器扩展。

 

模式

主要使用的是“模板方法”的设计模式。

 

本文章的功能优缺点

1.可以自定义生成缩略图的大小,任意定义。对于像微生活运动户外商城(http://sports.8t8x.com/) 、淘宝网等的网站,他们需要上传大量的商品图片时,非常有用。

2.缺点,我对System.Drawing的命名空间不太熟练,生成图像的方法还是从网上抄的,我觉得我自己得到的这些生成图像的方法,不是非常好。

 

代码实现

1.接口定义

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace CCNF.Plugin.Upload
{
    
/// <summary>
    
/// 上传功能的接口
    
/// </summary>
    
/// <creator>Marc</creator>
    public interface IUpload
    {
        
/// <summary>
        
/// 上传单个文件。
        
/// </summary>
        
/// <param name="sourcefile"></param>
        
/// <returns></returns>
        
/// <author>Marc</author>
        int SaveAs(HttpPostedFile sourcefile);
    }
}

 

2.抽象模板方法类

 由于使用代码插入的方式,cnblogs会报错,所以, 我不得不使用原始的copy方式,可能看起来会不太舒服。

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using System.IO;
using System.Net;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Web;

using System.Collections;

namespace CCNF.Plugin.Upload
{
    /// <summary>
    /// 上传功能。
    /// 本类提供上传的一般性方法。
    /// </summary>
    /// <creator>Marc</creator>
    public abstract class UploadAbstract : IUpload
    {
        #region 常量属性
        /// <summary>
        /// 允许上传的文件扩展名。
        /// 多个文件扩展名以英文逗号隔开。
        /// 默认从Web.config中获取。
        /// </summary>
        private readonly string UPLOADEXTENTION = ConfigurationManager.AppSettings["UploadExtention"];
        private string uploadExtention = null;
        /// <summary>
        /// 允许上传的文件扩展名。
        /// 多个文件扩展名以英文逗号隔开。
        /// 默认从Web.config中获取。
        /// </summary>
        public string UploadExtention
        {
            get
            {
                if (string.IsNullOrEmpty(this.uploadExtention))
                {
                    if (string.IsNullOrEmpty(UPLOADEXTENTION))
                    {
                        throw new Exception("web.config中未配置UploadExtention属性");
                    }

                    this.uploadExtention = UPLOADEXTENTION;
                }

                return this.uploadExtention;
            }
            set
            {
                this.uploadExtention = value;
            }
        }

        /// <summary>
        /// 允许上传的单个文件最大大小。
        /// 单位为k。
        /// 默认从Web.config中获取。
        /// </summary>
        private readonly int UPLOADLENGTH = Convert.ToInt16(ConfigurationManager.AppSettings["UploadLength"]);
        private int uploadLength = 0;
        /// <summary>
        /// 允许上传的单个文件最大大小。
        /// 单位为。
        /// 默认从Web.config中获取。
        /// </summary>
        public int UploadLength
        {
            get
            {
                if (this.uploadLength == 0)
                {
                    this.uploadLength = UPLOADLENGTH;
                }

                return this.uploadLength;
            }
            set
            {
                this.uploadLength = value;
            }
        }

        /// <summary>
        /// 所上传的文件要保存到哪个物理盘上。
        /// 此值为严格的物理文件夹路径。如:E:\CCNF\
        /// 注意:必须有盘符。
        /// 此属于用于扩展图片服务器数据存储。
        /// 默认从Web.config中获取。
        /// </summary>
        private readonly string UPLOADPHYSICALPATH = ConfigurationManager.AppSettings["UploadPhysicalPath"];
        private string uploadPhysicalPath = null;
        /// <summary>
        /// 所上传的文件要保存到哪个物理盘上。
        /// 此值为严格的物理文件夹路径。如:E:\CCNF\
        /// 注意:必须有盘符。
        /// 此属性用于扩展图片服务器数据存储。
        /// 默认从Web.config中获取。
        /// </summary>
        public string UploadPhysicalPath
        {
            get
            {
                if (string.IsNullOrEmpty(this.uploadPhysicalPath))
                {
                    if (string.IsNullOrEmpty(UPLOADPHYSICALPATH))
                    {
                        throw new Exception("web.config中未配置UploadPhysicalPath属性");
                    }
                    this.uploadPhysicalPath = UPLOADPHYSICALPATH;
                }

                return this.uploadPhysicalPath;
            }
            set
            {
                this.uploadPhysicalPath = value;
            }
        }
        #endregion

        #region 枚举
        /// <summary>
        /// 水印类型
        /// </summary>
        public enum WatermarkTypeEnum
        {
            /// <summary>
            /// 文字水印
            /// </summary>
            String = 1,
            /// <summary>
            /// 图片水印
            /// </summary>
            Image = 2
        }

        /// <summary>
        /// 上传结果
        /// </summary>
        protected enum UploadResultEnum
        {
            /// <summary>
            /// 未指定要上传的对象
            /// </summary>
            UploadedObjectIsNull = -9,
            /// <summary>
            /// 文件扩展名不允许
            /// </summary>
            ExtentionIsNotAllowed = -2,
            /// <summary>
            /// 文件大小不在限定范围内
            /// </summary>
            ContentLengthNotWithinTheScope = -1,
            /// <summary>
            /// 未配置或未指定文件的物理保存路径
            /// </summary>
            UploadPhysicalPathNoSpecify = -20,
            /// <summary>
            /// 未指定图片水印的相对文件物理路径
            /// </summary>
            ImageWartermarkPathNoSpecify = -30,
            /// <summary>
            /// 未指定水印的文字
            /// </summary>
            StringWatermarkNoSpecify = -31,
            /// <summary>
            /// 上传原始文件失败
            /// </summary>
            UploadOriginalFileFailure = 0,
            /// <summary>
            /// 生成缩略失败
            /// </summary>
            CreateThumbnailImageFailure = -3,
            /// <summary>
            /// 未知错误
            /// </summary>
            UnknownError = -4,
            /// <summary>
            /// 上传成功
            /// </summary>
            Success = 1
        }
        #endregion

        #region 上传属性
        /// <summary>
        /// 保存文件夹。
        /// 格式形如: upload\   或 images\  或 upload\user\ 等。以\结尾。
        /// 不允许加盘符
        /// </summary>
        public string SaveFolder { get; set; }

        /// <summary>
        /// 自定义生成新的文件夹。
        /// 格式形如: upload\   或 images\  或 upload\2011\10\8\ 等。以\结尾。
        /// 最终的文件夹 = UploadPhysicalPath + SaveFolder + Folder
        /// </summary>
        public string Folder { get; set; }

        /// <summary>
        /// 是否生成水印。
        /// 默认不启用水印生成。
        /// </summary>
        public bool IsMakeWatermark { get; set; }

        private int watermarkType = (int)WatermarkTypeEnum.String;
        /// <summary>
        /// 生成水印的方式:string从文字生成,image从图片生成。
        /// 默认是文字水印
        /// </summary>
        public int WatermarkType
        {
            get
            {
                return this.watermarkType;
            }
            set
            {
                this.watermarkType = value;
            }
        }

        /// <summary>
        /// 水印文字。
        /// </summary>
        public string Watermark { get; set; }

        /// <summary>
        /// 水印图片的位置。
        /// 提供图片水印的相对位置。
        /// 不含盘符。
        /// </summary>
        public string ImageWartermarkPath { get; set; }

        /// <summary>
        /// 上传后生成的新文件路径。
        /// 此路径为相对物理路径,不含盘符。
        /// </summary>
        public string NewFilePath { get; protected set; }

        /// <summary>
        /// 生成缩略图片的长宽, 是一个二维数据。 
        /// 如:int a[3,2]={{1,2},{5,6},{9,10}}。
        /// 如果上传的文件是图片类型,并且希望生成此图片的缩略图,那么请将需要生成的长宽尺寸以数组的方式保存到这里。
        /// </summary>
        public int[,] PicSize { get; set; }

        /// <summary>
        /// 生成的图片地址,与二维数组PicSize的长度是对应的。
        /// 如果有传入PicSize的数组,那么上传完毕后,系统会返回PicPath数组。
        /// 这个数组是它的最终上传路径,以便用户对此值进行数据库操作。
        /// 产生的PicPath的索引位置与PicSize是一一对应的。
        /// 此属性已声明为只读属性。
        /// </summary>
        public string[] PicPath { get; protected set; }
        #endregion

        /// <summary>
        /// 上传单个文件
        /// </summary>
        /// <param name="sourcefile"></param>
        /// <param name="upload"></param>
        /// <returns></returns>
        /// <author>Marc</author>
        public virtual int SaveAs(HttpPostedFile sourcefile)
        {
            int result = 0;

            //未知错误
            UploadResultEnum uploadResultEnum = UploadResultEnum.UnknownError;
            if (sourcefile == null)
            {
                //未指定要上传的对象
                uploadResultEnum = UploadResultEnum.UploadedObjectIsNull;
            }
            else
            {
                uploadResultEnum = Upload(sourcefile);
            }

            result = (int)uploadResultEnum;

            return result;
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="sourcefile"></param>
        /// <returns></returns>
        /// <author>Marc</author>
        private UploadResultEnum Upload(HttpPostedFile sourcefile)
        {
            #region 上传前检测
            if (string.IsNullOrEmpty(UploadPhysicalPath))
            {
                //未配置或未指定文件的物理保存路径
                return UploadResultEnum.UploadPhysicalPathNoSpecify;
            }

            string fileName = sourcefile.FileName;
            string fileExtention = Path.GetExtension(fileName);

            if (!CheckExtention(fileExtention))
            {
                //文件扩展名不允许
                return UploadResultEnum.ExtentionIsNotAllowed;
            }

            int fileLength = sourcefile.ContentLength;

            if (fileLength <= 0 || fileLength > UploadLength * 1024)
            {
                //文件大小不在限定范围内
                return UploadResultEnum.ContentLengthNotWithinTheScope;
            }

            //创建要保存的文件夹
            string physicalPath = UploadPhysicalPath + SaveFolder + Folder;
            if (!Directory.Exists(physicalPath))
            {
                Directory.CreateDirectory(physicalPath);
            }
            #endregion

            string newFileName = "Ori_" + Guid.NewGuid().ToString() + fileExtention;
            //新文件相对物理路径
            string newFilePath = physicalPath + newFileName;

            #region 保存原始文件
            if (!IsMakeWatermark) //不启用水印时
            {
                //保存文件
                sourcefile.SaveAs(newFilePath);
            }
            else //要求生成水印
            {
                Image bitmap = new System.Drawing.Bitmap(sourcefile.InputStream);
                Graphics g = Graphics.FromImage(bitmap);
                g.InterpolationMode = InterpolationMode.High;
                g.SmoothingMode = SmoothingMode.AntiAlias;
                Image fromImg = null;

                try
                {
                    //清除整个绘图面并以透明背景色填充
                    //g.Clear(Color.Transparent);
                    if (WatermarkType == (int)WatermarkTypeEnum.String) //生成文字水印
                    {
                        if (string.IsNullOrEmpty(Watermark))
                        {
                            //未指定水印的文字
                            return UploadResultEnum.StringWatermarkNoSpecify;
                        }

                        Color color = Color.FromArgb(120, 255, 255, 255);
                        Brush brush = new SolidBrush(color);

                        // 自动根据界面来缩放字体大小
                        int desiredWidth = (int)(bitmap.Width * .5);
                        Font font = new Font("Arial", 16, FontStyle.Regular);
                        SizeF stringSizeF = g.MeasureString(Watermark, font);
                        float Ratio = stringSizeF.Width / font.SizeInPoints;
                        int requiredFontSize = (int)(desiredWidth / Ratio);

                        // 计算图片中点位置
                        Font requiredFont = new Font("Arial", requiredFontSize, FontStyle.Bold);
                        stringSizeF = g.MeasureString(Watermark, requiredFont);
                        int x = desiredWidth - (int)(stringSizeF.Width / 2);
                        int y = (int)(bitmap.Height * .5) - (int)(stringSizeF.Height / 2);

                        g.DrawString(Watermark, new Font("Verdana", requiredFontSize, FontStyle.Bold), brush, new Point(x, y));
                        bitmap.Save(newFilePath, ImageFormat.Jpeg);
                    }
                    else if (WatermarkType == (int)WatermarkTypeEnum.Image) //生成图片水印
                    {
                        if (string.IsNullOrEmpty(ImageWartermarkPath))
                        {
                            //未指定图片水印的相对文件物理路径
                            return UploadResultEnum.ImageWartermarkPathNoSpecify;
                        }

                        fromImg = Image.FromFile(ImageWartermarkPath);

                        g.DrawImage(fromImg, new Rectangle(bitmap.Width - fromImg.Width, bitmap.Height - fromImg.Height, fromImg.Width, fromImg.Height), 0, 0, fromImg.Width, fromImg.Height, GraphicsUnit.Pixel);

                        bitmap.Save(newFilePath, ImageFormat.Jpeg);
                    }
                }
                catch
                {
                    //上传原始文件失败
                    return UploadResultEnum.UploadOriginalFileFailure;
                }
                finally
                {
                    if (fromImg != null)
                    {
                        fromImg.Dispose();
                    }
                    g.Dispose();
                    bitmap.Dispose();
                }
            }
            #endregion

            this.NewFilePath = newFilePath.Replace("\\", "/");

            #region 生成各种规格的缩略图
            //生成各种规格的缩略图
            if (PicSize != null && PicSize.Length > 0)
            {
                int width, height;

                ArrayList picPathArray = new ArrayList();

                for (int i = 0; i < PicSize.GetLength(0); i++)
                {
                    width = PicSize[i, 0];
                    height = PicSize[i, 1];

                    Guid tempGuid = Guid.NewGuid();

                    //缩略图名称
                    string thumbnailFileName = physicalPath + tempGuid.ToString() + "_" + width.ToString() + "X" + height.ToString() + fileExtention;

                    if (!SaveThumb(sourcefile, width, height, thumbnailFileName))
                    {
                        //生成缩略失败
                        return UploadResultEnum.CreateThumbnailImageFailure;
                    }

                    picPathArray.Add(thumbnailFileName.Replace("\\", "/"));
                }

                PicPath = (string[])picPathArray.ToArray(typeof(string));
            }
            #endregion

            //上传成功
            return UploadResultEnum.Success;
        }

        /// <summary>
        /// 生成缩略图
        /// </summary>
        /// <param name="sourcefile"></param>
        /// <param name="width">生成缩略图的宽度</param>
        /// <param name="height">生成缩略图的高度</param>
        /// <param name="thumbnailFileName">缩略图生成路径,含盘符的绝对物理路径。</param>
        /// <returns></returns>
        /// <author>Marc</author>
        private bool SaveThumb(HttpPostedFile sourcefile, int width, int height, string thumbnailFileName)
        {
            bool result = false;
            Image ori_img = Image.FromStream(sourcefile.InputStream);

            int ori_width = ori_img.Width;//650
            int ori_height = ori_img.Height;//950

            int new_width = width;//700
            int new_height = height;//700

            // 在此进行等比例判断,公式如下:
            if (new_width > ori_width)
            {
                new_width = ori_width;
            }
            if (new_height > ori_height)
            {
                new_height = ori_height;
            }
            if ((double)ori_width / (double)ori_height > (double)new_width / (double)new_height)
            {
                new_height = ori_height * new_width / ori_width;
            }
            else
            {
                new_width = ori_width * new_height / ori_height;
            }

            Image bitmap = new System.Drawing.Bitmap(new_width, new_height);
            Graphics g = Graphics.FromImage(bitmap);
            try
            {
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.InterpolationMode = InterpolationMode.High;
                g.Clear(System.Drawing.Color.Transparent);
                g.DrawImage(ori_img, new Rectangle(0, 0, new_width, new_height), new Rectangle(0, 0, ori_width, ori_height), GraphicsUnit.Pixel);

                bitmap.Save(thumbnailFileName, ImageFormat.Jpeg);
                result = true;
            }
            catch
            {
                result = false;
            }
            finally
            {
                if (ori_img != null)
                {
                    ori_img.Dispose();
                }
                if (bitmap != null)
                {
                    bitmap.Dispose();
                }
                g.Dispose();
                bitmap.Dispose();
            }

            return result;
        }

        /// <summary>
        /// 检查文件扩展名
        /// </summary>
        /// <param name="extention"></param>
        /// <returns></returns>
        protected bool CheckExtention(string extention)
        {
            bool b = false;

            string[] extentions = this.UploadExtention.Split(',');
            for (int i = 0; i < extentions.Length; i++)
            {
                if (extention.ToLower() == extentions[i].ToLower())
                {
                    b = true;
                    break;
                }
            }

            return b;
        }

        /// <summary>
        /// 返回上传结果
        /// </summary>
        /// <param name="result">消息集合</param>
        /// <returns></returns>
        /// <author>marc</author>
        public string Error(int[] result)
        {
            string s = "";
            for (int i = 0; i < result.Length; i++)
            {
                s += "第" + (i + 1).ToString() + "张:";
                if (result[i] != 1)
                {
                    switch (result[i])
                    {
                        case (int)UploadResultEnum.UploadedObjectIsNull:
                            s += "未指定要上传的对象";
                            break;
                        case (int)UploadResultEnum.ExtentionIsNotAllowed:
                            s += "文件扩展名不允许";
                            break;
                        case (int)UploadResultEnum.ContentLengthNotWithinTheScope:
                            s += "文件大小不在限定范围内";
                            break;
                        case (int)UploadResultEnum.UploadPhysicalPathNoSpecify:
                            s += "未配置或未指定文件的物理保存路径";
                            break;
                        case (int)UploadResultEnum.ImageWartermarkPathNoSpecify:
                            s += "未指定图片水印的相对文件物理路径";
                            break;
                        case (int)UploadResultEnum.StringWatermarkNoSpecify:
                            s += "未指定水印的文字";
                            break;
                        case (int)UploadResultEnum.UploadOriginalFileFailure:
                            s += "上传原始文件失败";
                            break;
                        case (int)UploadResultEnum.CreateThumbnailImageFailure:
                            s += "生成缩略失败";
                            break;
                        case (int)UploadResultEnum.UnknownError:
                            s += "未知错误";
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    s += "上传成功";
                }

                s += ";\\r\\n";
            }

            return s;
        }

        /// <summary>
        /// 返回上传结果
        /// </summary>
        /// <param name="result">消息值,int型</param>
        /// <returns></returns>
        /// <author>marc</author>
        public string Error(int result)
        {
            string s = null;

            switch (result)
            {
                case (int)UploadResultEnum.UploadedObjectIsNull:
                    s += "未指定要上传的对象";
                    break;
                case (int)UploadResultEnum.ExtentionIsNotAllowed:
                    s += "文件扩展名不允许";
                    break;
                case (int)UploadResultEnum.ContentLengthNotWithinTheScope:
                    s += "文件大小不在限定范围内";
                    break;
                case (int)UploadResultEnum.UploadPhysicalPathNoSpecify:
                    s += "未配置或未指定文件的物理保存路径";
                    break;
                case (int)UploadResultEnum.ImageWartermarkPathNoSpecify:
                    s += "未指定图片水印的相对文件物理路径";
                    break;
                case (int)UploadResultEnum.StringWatermarkNoSpecify:
                    s += "未指定水印的文字";
                    break;
                case (int)UploadResultEnum.UploadOriginalFileFailure:
                    s += "上传原始文件失败";
                    break;
                case (int)UploadResultEnum.CreateThumbnailImageFailure:
                    s += "生成缩略失败";
                    break;
                case (int)UploadResultEnum.UnknownError:
                    s += "未知错误";
                    break;
                case (int)UploadResultEnum.Success:
                    s += "上传成功";
                    break;
                default:
                    break;
            }

            return s;
        }
    }
}

 

所有的实现功能都在这个模板方法中。

主要定义了“常量属性”、“枚举”、“上传属性”, 以及开放方法 SaveAs(HttpPostedFile sourcefile) 和 返回错误消息的方法。

 

3.具体抽象类的实现,很简单,请看

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace CCNF.Plugin.Upload
{
    
/// <summary>
    
/// 上传文件
    
/// </summary>
    
/// <creator>Marc</creator>
    public sealed class Upload : UploadAbstract
    {

    }
}

 

 4.前台处理页面Upload.ashx,注意是处理页面,ashx文件。

<%@ WebHandler Language="C#" Class="Upload" %>

using System;
using System.Web;
using System.IO;

public class Upload : IHttpHandler
{
    
public void ProcessRequest(HttpContext context)
    {
        HttpResponse response 
= context.Response;
        HttpRequest request 
= context.Request;
        HttpServerUtility server 
= context.Server;

        context.Response.ContentType 
= "text/plain";

        HttpPostedFile httpPostedFile 
= request.Files["FileData"];
        
string uploadpath = request["folder"+ "\\";// server.MapPath(request["folder"] + "\\");

        
if (httpPostedFile != null)
        {
            CCNF.Plugin.Upload.Upload upload 
= new CCNF.Plugin.Upload.Upload();
            upload.SaveFolder 
= uploadpath;
            upload.PicSize 
= new int[,] { { 200150 } };//生成缩略图,要生成哪些尺寸规格的缩略图,请自行定义二维数组
            int result = upload.SaveAs(httpPostedFile);

            
if (result == 1)
            {
                context.Response.Write(
"1");
            }
            
else
            {
                
throw new Exception(upload.Error(result));
                
// context.Response.Write("0");
            }
        }
    }

    
public bool IsReusable
    {
        
get
        {
            
return false;
        }
    }
}

 5.前台aspx页面调用ashx页面

这里,我使用了一个第三方控件,因为这不是我本文的重点,所以,只简略说一下这个控件的名字是:jquery.uploadify,各位可以去找一下这个js控件的代码。使用起来很简单的的一个js封装控件。

通过这个js控件,会将页面上传的文件post到ashx处理文件中,ashx会接收数据并开始上传。

 

后记

一、上传功能遍地都是, 而本文主要的亮点在于自定义生成多个缩略图。这对于像微生活运动户外商城(http://sports.8t8x.com/) 、淘宝网等的网站,非常适用。

二、我再次回顾设计模式,温故而知新,又有新的发现,所以,发此文,聊表慰藉。

 

关于本文作者

马志远(Marc),1981年,2002年湖北大学肄业,现蜗居广州。2004年学习编程,至今已经有8年的编程经验,长期从事asp.net B/S方面的开发和设计。在项目解决方案上、在项目性能和扩展等上,具有深强的能力。您可以使用mazhiyuan1981@163.com与我取得联系。
 

 

posted @ 2011-09-16 10:50    阅读(3231)  评论(11编辑  收藏  举报