大家好,今天和大家分享下无刷新上传大文件文件东西,先说明只讲关键部分.
大概如下图:
一,目标
(1)有3种默认颜色风格
(2)支持大文件上传
(3)可以中途停止
(4)有进度信息提示
二,简述
一个文件在http下发送整体格式是:
-----------------------------7d51f321004ec
Content-Disposition: form-data; name="guid"
ea0032acb80b210becea0032acb80b2
-----------------------------7d51f321004ec
-----------------------------7d51f321004ec
Content-Disposition: form-data; name="m_file"; filename="D:/mypic/logo.png"
Content-Type: image/x-png
?PNG
IHDR ] & ¦??Á gAMA ¯È7?é tEXtSoftware Adobe ImageReadyqÉe< ÂIDATxÚbüÿÿ?Ã0 ?@Ìļ@ü?¿fÇã0 t`b1 V?FÂ] ~>
@Ã)Ð?S½:?@Sÿ ¾ Äÿ?h8:2Ðbs â¯@| ?/´£ h¸ : ?ÊúP â@<??c h¤: °q§ñ' ?Ä?ø=@#-ÐÑS~"óA~_¡?å 4R¹Ò9Ý@,ÄË?¸?¯ÒÒR? é?ü@/ ÄeÐÏ2 n£UàÐh £P? ?Ó???7 ñb ÞLMK h4бE ?ÄPþV îâCÔ0 ???Ø<@, d8èà±2hf3ý>{q
-----------------------------7d51f321004ec--
大概是如上,一部分信息被删除,为的是简单起见...
最主要是是把乱码部分的内容用流二进制把它保存起来,并且写到文件中,就是达到上传的目的了..
至于实现过程我接下来我们慢慢的分析
(1)在B/S模式中,当文件在FORM中POST到服务机的过程中.我们一般都是在MOUDULE中把请求截下来..
internal abstract class BaseModule : IHttpModule
{
#region IHttpModule Members
public void Dispose()
{
// throw new NotImplementedException();
}
public void Init(HttpApplication context)
{
//context.BeginRequest += new EventHandler(context_BeginRequest);
context.AuthenticateRequest += new EventHandler(context_BeginRequest);
//context.AuthorizeRequest += new EventHandler(context_AcquireRequestState);
context.AuthenticateRequest+= new EventHandler(context_AcquireRequestState);
//context.AuthorizeRequest += new EventHandler(context_BeginRequest);
context.EndRequest += new EventHandler(context_EndRequest);
// context.Error += new EventHandler(context_Error);
}
void context_AcquireRequestState(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
MySessionBegin(app, e);
}
void context_Error(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
MyError(app, e);
}
void context_EndRequest(object sender, EventArgs e)
{
HttpApplication app =sender as HttpApplication;
MyEndRequest(app,e);
}
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
MyBeginRequest(app, e);
}
#endregion
public abstract void MyBeginRequest(HttpApplication app, EventArgs e);
public abstract void MyEndRequest(HttpApplication app, EventArgs e);
public abstract void MyError(HttpApplication app, EventArgs e);
public abstract void MySessionBegin(HttpApplication app, EventArgs e);
}
继承上面的抽象类.实现module...
internal class WebModule : BaseModule
{
private LogHelper log = null;
private string tmpfolder = "~tmpfile";
public WebModule()
{
}
public override void MyBeginRequest(HttpApplication app, EventArgs e)
{
//在这里截信息...
}
public override void MySessionBegin(HttpApplication app, EventArgs e)
{
this.UploadMethod(app);
}
public override void MyEndRequest(HttpApplication app, EventArgs e)
{
//throw new NotImplementedException();
if(this.log!=null) log.Close();
}
public override void MyError(HttpApplication app, EventArgs e)
{
this.Dispose();
}
#region 方法
private HttpWorkerRequest GetWorkerRequest(HttpApplication app)
{
IServiceProvider m_provider = app.Context;
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
return ((HttpWorkerRequest)m_provider.GetService(typeof(HttpWorkerRequest)));
}
private HttpWorkerRequest GetWorkerRequest()
{
IServiceProvider m_provider = HttpContext.Current;
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
return ((HttpWorkerRequest)m_provider.GetService(typeof(HttpWorkerRequest)));
}
/// <summary>
/// Get the upload file max Size from web.config.
/// </summary>
/// <returns></returns>
private long GetUpLoadFileLength()
{
int m_MaxLength = 0;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(Path.Combine(HttpContext.Current.Request.PhysicalApplicationPath, "web.config"));
XmlNode node = xmlDocument.SelectSingleNode("configuration/system.web/httpRuntime/@maxRequestLength");
if (node != null)
{
m_MaxLength = Convert.ToInt32(node.Value);
}
else
{
m_MaxLength = 1024;//1MB for default;
}
xmlDocument = null;
return (m_MaxLength * 1024);
}
private bool IsUseSession()
{
string re = System.Configuration.ConfigurationManager.AppSettings["MarkUploadSession"];
if (re == null || re == "") return false;
bool t=true;
if (bool.TryParse(re, out t))
return t;
else
return false;
}
private string GetTmpFolder()
{
string re= System.Configuration.ConfigurationManager.AppSettings["MarkUpload"];
return (re!=null) ? re : "~/markupload/";
}
private string GetKeyofMD()
{
string re=System.Configuration.ConfigurationManager.AppSettings["keyOfMD"];
return re;
}
private bool CheckRefer(HttpApplication app)
{
string host =@"http://" + app.Request.Url.Host.ToLower();
Uri obj=app.Request.UrlReferrer;
if (obj == null) return false;
string refer = obj.ToString().ToLower();
return (refer.IndexOf(host) == 0);
}
private bool UploadMethod(HttpApplication app)
{
//this.log = new LogHelper("G://IIS_Path//TestMarkUpload//tmpfile_");
//log.MaxSize = 1024 * 1024;
//MyEncoder.encoding_ = app.Request.ContentEncoding;
//log.Encod_write = MyEncoder.encoding_;
//log.DateTimeFormat = "ms";
//log.Write(app.Request.Url.AbsoluteUri);
HttpWorkerRequest worker = this.GetWorkerRequest(app);
string ct = worker.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
if (ct != null && string.Compare(ct, 0, MARKER.C_MARKER, 0, MARKER.C_MARKER.Length, true) == 0)
{
long total = long.Parse(worker.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentLength));
if (total > 0)
{
WorkType WT = new WorkType();
byte[] buf = new byte[WT.Size];
FormStream fs = null;
ReturnInfo Rinf = new ReturnInfo();
try
{
string boundary = "--" + ct.Substring(ct.IndexOf(MARKER.B_MARKER) + MARKER.B_MARKER.Length);
fs = new FormStream(boundary, app.Request.ContentEncoding, total, this.GetKeyofMD(), app.Server.MapPath(this.GetTmpFolder()));
fs.OnCreateFile += new FormStream.HandlerCreateFile(fs_OnCreateFile);
fs.OnCloased += new FormStream.HandlerOnClose(fs_OnCloased);
if (worker.GetPreloadedEntityBodyLength() > 0)
{
byte[] data = worker.GetPreloadedEntityBody();
total -= data.Length;
fs.Write(data, 0, data.Length);
}
while (total > 0 && worker.IsClientConnected() && total >= buf.Length)
{
if(WT.IsUseThread) System.Threading.Thread.Sleep(1);
int read = worker.ReadEntityBody(buf, buf.Length);
if (read > 0)
{
total -= read;
fs.Write(buf, 0, read);
}
else
break;
}
if (total > 0 && worker.IsClientConnected() && total < buf.Length)
{
int read = worker.ReadEntityBody(buf, buf.Length);
if (read > 0)
{
total -= read;
fs.Write(buf, 0, read);
}
}
Rinf.Id = 1;
Rinf.Ftotal = fs.Position - fs.Headlen-fs.EOF.Length-fs.CRLF.Length;
Rinf.Message = fs.FieldName["guid"];
}
catch (Exception e)
{
Rinf.Id = (e.Message == "stopping") ? -5 : -1;
Rinf.Ftotal = -3;
Rinf.Message = e.Message;
//log.Write(e.ToString());
}
finally
{
fs.Close();
}
app.Response.ContentType = "text/html";
app.Response.Write(JsonUtil.ToJsonString(Rinf));
app.Response.End();
return true;
}
}
//this.log.Close();
return false;
}
void fs_OnCloased(string guid)
{
HttpContext.Current.Application.Remove(guid);
}
void fs_OnCreateFile(Info info_,string guid)
{
HttpContext.Current.Application.Add(guid, info_);
}
private string ReplaceSpecal(string src)
{
if (src == null) return "";
return src.Replace("/"", "").Replace("//","").Replace("/","").Replace("'", "").Replace(";", "").Replace("?", "").Replace(")", "").Replace("(", "").Replace(":", "");
}
#endregion
}
}
至于如何设置module的东西我就不讲了...只讲下如何把流读出来(上面代码的UploadMethod函数就是度取流的函数,)并且把读到的流方如这么一个类
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Collections;
namespace MarkUpload
{
internal class FormStream : Stream, IDisposable
{
public delegate void HandlerCreateFile(Info info_,string guid);
public delegate void HandlerOnClose(string guid);
public event HandlerCreateFile OnCreateFile;
public event HandlerOnClose OnCloased;
#region Declara
Info info_ = null;
/// <summary>
/// 进度条信息
/// </summary>
public Info Info_
{
get { return info_; }
}
Speeds sp = new Speeds();
Encoding _encoding;
MemoryStream _headbytes;
private long headlen;
/// <summary>
/// 头部实际长度
/// </summary>
public long Headlen
{
get { return headlen; }
set { headlen = value; }
}
/// <summary>
/// 总信息长度
/// </summary>
long Total = 0;
/// <summary>
/// 当前长度
/// </summary>
long _position=0;
/// <summary>
/// 结束整个流字符标记,也就是BOUNDARY+“--”
/// </summary>
public byte[] EOF;
/// <summary>
/// Field开始标记行
/// </summary>
byte[] BOUNDARY;
/// <summary>
/// 标记头和实体的间隔行
/// </summary>
byte[] EOH;
/// <summary>
/// 某个实体行的结束标记行
/// </summary>
public byte[] CRLF;
byte[] MD_;
/// <summary>
/// 判是否等待头信息完成
/// </summary>
bool _headerNeeded=true;
/// <summary>
/// 判断是否是文件体部分了
/// </summary>
///
string key_ = null;
string tmp_path = null;
FileStream fs_ = null;
Dictionary<string, string> fieldName = new Dictionary<string, string>();
public Dictionary<string, string> FieldName
{
get { return fieldName; }
}
#endregion
#region Constructor
/// <summary>
///
/// </summary>
/// <param name="boundary">特殊分割符号</param>
/// <param name="encoding_">编码方式</param>
/// <param name="total_">总提交大小</param>
/// <param name="key">八位</param>
/// <param name="tmp_path_">FullPath</param>
public FormStream(string boundary, Encoding encoding_,long total_,string key,string tmp_path_)
{
headlen = 0;
this._encoding = encoding_;
this.BOUNDARY = this._encoding.GetBytes(boundary);
this.EOF = this._encoding.GetBytes(boundary + "--/r/n");
this.EOH = this._encoding.GetBytes("/r/n/r/n");
this.CRLF = this._encoding.GetBytes("/r/n");
this.MD_ = this._encoding.GetBytes(MARKER.MDSTR);
this.Total = total_;
this._headerNeeded=true;
this._headbytes = new MemoryStream();
this.key_ = key;
this.tmp_path = tmp_path_;
this.info_ = new Info();
}
#endregion
#region StreamMethod
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
if (this._headbytes != null && this._headbytes.CanWrite)
this._headbytes.Close();
if (this.OnCloased != null)
{
if (!this._headerNeeded)
this.OnCloased(this.FieldName["guid"]);
}
}
public override long Length
{
get { return this._position; }
}
public override long Position
{
get
{
return this._position;
}
set
{
this._position = value;
}
}
public void Close()
{
if (this.fs_ != null ) this.fs_.Close();
if (this.OnCloased != null)
{
try
{
string guid = this.FieldName["guid"];
this.OnCloased(guid);
}
catch (Exception e)
{ }
}
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
private LogHelper log = null;
public LogHelper Log
{
get {
return log; }
set { this.log = value; }
}
public override void Write(byte[] buffer, int offset, int count)
{
if (count <= 0 ) return;
if (this.Total > 0 && this.Total <= this.Position) return;
if (this._headerNeeded)//读头部信息
{
this._headbytes.Write(buffer, offset, count);
byte[] buf = this._headbytes.GetBuffer();
Dictionary<int,int> p=this.CheckHeadField(buf);
if (p.Count == 3)//当3段都填写了之后
{
this.FillTheHead(buf, p);
}
}
else//写文件
{
long will=this._position+count;
long file_end_p=this.Total-this.EOF.Length-this.CRLF.Length;
if ((this._position + count) <= file_end_p)
{
this.fs_.Write(buffer, offset, count);
}
else
{
int shouldWrite=int.Parse((file_end_p - this._position).ToString());
this.fs_.Write(buffer, 0,shouldWrite);
count = shouldWrite;
}
}
this._position += count;
this.info_.Current = this._position;
if (this.info_.Total < 0)
{
this.fs_.Close();
File.Delete(Path.Combine(this.tmp_path, this.FieldName["guid"] + Path.GetExtension(this.FieldName["filename"])));
throw new Exception("stopping");
}
this.sp.Current +=count;
TimeSpan ts = DateTime.Now.Subtract(sp.DT);
if(ts.TotalSeconds>=1)
{
this.info_.Speed = (double)(sp.Current / 1024);
this.sp.Current = 0;
this.sp.DT = DateTime.Now;
}
}
#endregion
#region 自定义方法
private int IndexOf(byte[] buffer, byte[] checFor)
{
return this.IndexOf(buffer, checFor, 0,buffer.Length);
}
/// <summary>
/// 取得checkFor字节数组在buffer出现的位置,不存在则返回-1
/// </summary>
/// <param name="buffer"></param>
/// <param name="checkFor"></param>
/// <param name="start_"></param>
/// <returns></returns>
private int IndexOf(byte[] buffer, byte[] checkFor,int start_)
{
return this.IndexOf(buffer, checkFor, start_, buffer.Length - start_);
}
private int IndexOf(byte[] buffer, byte[] checkFor, int start, int count)
{
int index = 0;
int startPos = Array.IndexOf(buffer, checkFor[0], start);
if (startPos != -1)
{
while ((startPos + index) < buffer.Length)
{
if (buffer[startPos + index] == checkFor[index])
{
index++;
if (index == checkFor.Length)
{
return startPos;
}
}
else
{
startPos = Array.IndexOf<byte>(buffer, checkFor[0], startPos + index);
if (startPos == -1)
{
return -1;
}
index = 0;
}
}
}
return -1;
}
/// <summary>
/// 创建文件流
/// </summary>
/// <param name="buf">当前读取的头信息字节数组</param>
/// <param name="start">截取头信息字节数组中文件部分的开始位置</param>
private void CreateFileStrean(byte[] buf, int start)
{
string md = this.FieldName["mdstr"];
string guid=this.FieldName["guid"];
if (md == null || "" == md || guid == null || "" == guid)
{
throw new Exception("包格式错误");
}
else
{
//this.myExt + "@" + this.max_Size + "@" + DateTime.Now.ToString("dd");
string source = MD.Decode(md, this.key_);
string[] Mdpart = source.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries);
if (long.Parse(Mdpart[1]) < (this.Total - this.headlen))
throw new Exception("大小不匹配");
string filename_=this.FieldName["filename"];
string ext=Path.GetExtension(filename_);
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(Mdpart[0], System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if (!reg.IsMatch(ext))
{ throw new Exception("文件类型不匹配"); }
if (!Directory.Exists(this.tmp_path)) Directory.CreateDirectory(this.tmp_path);
this.fs_= File.Create(Path.Combine(this.tmp_path, guid + ext));
long File_End_Poision=this.Total - this.EOF.Length-this.CRLF.Length;
if ( File_End_Poision>= buf.Length)
{
this.fs_.Write(buf,start, buf.Length - start);
}
else
{
this.fs_.Write(buf, start,int.Parse((File_End_Poision - start).ToString()));
}
this._headbytes.Close();
if (this.OnCreateFile != null)
{
this.info_.Total = this.Total;
this.info_.Filename = Path.GetFileName(filename_);
this.OnCreateFile(this.info_, guid);
}
}
}
/// <summary>
/// 填写头信息
/// </summary>
/// <param name="buf">头信息的数组</param>
/// <param name="p">分段信息</param>
private void FillTheHead(byte[] buf,Dictionary<int,int> p)
{
foreach (int start in p.Keys)
{
int end=p[start];
string data = this._encoding.GetString(buf,start,end-start);
string[] tmp=System.Text.RegularExpressions.Regex.Split(data,";");
if (tmp.Length > 2)
{
// int index = tmp[1].IndexOf('"');
string name = "filename";
int index=tmp[2].IndexOf('"')+1;
string value = tmp[2].Substring(index, tmp[2].IndexOf('"', index + 1)-index);
this.FieldName.Add(name.ToLower(), value);
this.headlen = end +this.EOH.Length;
this._headerNeeded = false;
this.CreateFileStrean(buf, end + this.EOH.Length);
break;
}
else
{
int index = tmp[1].IndexOf('"')+1;
string name = tmp[1].Substring(index, tmp[1].IndexOf('"', index + 1) - index);
int start_=end+this.EOH.Length;
int end_=this.IndexOf(buf,this.BOUNDARY,start_);
string field_data = this._encoding.GetString(buf, start_, end_ - start_ - this.CRLF.Length);
this.FieldName.Add(name.ToLower(), field_data);
}
}
}
/// <summary>
/// 取得头信息各分段
/// </summary>
/// <param name="buf">头信息的字节数组</param>
/// <returns>各字段的分段信息</returns>
private Dictionary<int,int> CheckHeadField(byte[] buf)
{
int start = 0;
Dictionary<int, int> p = new Dictionary<int, int>();
while(true)
{
int boundary_p = this.IndexOf(buf, this.BOUNDARY, start);
int end = this.IndexOf(buf, this.EOH, boundary_p+this.BOUNDARY.Length);
if (boundary_p>=0 && (end-boundary_p) > 0)
{
p.Add(boundary_p+this.BOUNDARY.Length,end);
start = end;
}
else
{
break;
}
}
if (buf.Length >= 409600) throw new Exception("Head too long or can not be load");
return p;
}
#endregion
}
}
这样可能有点乱..大家先凑合着看吧,过些天在把源代码传上..急要源代码请留下邮箱吧..