asp.net文件上传功能(单文件,多文件,自定义生成缩略图,水印)
程序员文章站
2024-03-06 19:11:20
前言 上传功能,是大家经常用到了,可能每一个项目都可以会用到。网上到处都有上传功能的代码。比我写的好的有很多。我这里也仅是分享我的代码。 功能实现点 1.单个文件上传; 2...
前言
上传功能,是大家经常用到了,可能每一个项目都可以会用到。网上到处都有上传功能的代码。比我写的好的有很多。我这里也仅是分享我的代码。
功能实现点
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[,] { { 200, 150 } };//生成缩略图,要生成哪些尺寸规格的缩略图,请自行定义二维数组
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与我取得联系。
上传功能,是大家经常用到了,可能每一个项目都可以会用到。网上到处都有上传功能的代码。比我写的好的有很多。我这里也仅是分享我的代码。
功能实现点
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[,] { { 200, 150 } };//生成缩略图,要生成哪些尺寸规格的缩略图,请自行定义二维数组
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与我取得联系。
推荐阅读
-
asp.net文件上传功能(单文件,多文件,自定义生成缩略图,水印)
-
Struts2实现单文件或多文件上传功能
-
Struts2实现单文件或多文件上传功能
-
asp.net文件上传解决方案(图片上传、单文件上传、多文件上传、检查文件类型)
-
图片上传类;支持水印-日期文件夹-生成缩略图 ,支持多文件上传,给力分享
-
asp.net文件上传解决方案(图片上传、单文件上传、多文件上传、检查文件类型)
-
ASP.NET Core单文件和多文件上传并保存到服务端的方法
-
ASP.NET Core单文件和多文件上传并保存到服务端的方法
-
struts2上传文件、生成缩略图、添加文字和图片水印
-
图片上传类;支持水印-日期文件夹-生成缩略图 ,支持多文件上传,给力分享