前言 上傳功能,是大家經常用到了,可能每一個項目都可以會用到。網上到處都有上傳功能的代碼。比我寫的好的有很多。我這裡也僅是分享我的代碼。
功能實現點
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方面的開發和設計。在項目解決方案上、在項目性能和擴展等上,具有深強的能力。您可以使用
[email protected]與我取得聯系。