// Filter the request data to get the file binary data. // Here is an algorithm logic to filter the data. There // will be a lot of words to say before we talk about // the algorithm(Maybe a small book). So I just open the // source here but omit the explanation in details. private void FilterFileDataFromBodyEntity(byte[] data) { if (data == null) { return; } if (draft != null) { byte[] temp = BinaryHelper.Combine(draft, BinaryHelper.Copy(data, 0, _boundary.Length)); int entity_st = BinaryHelper.SequenceIndexOf(temp, _boundary, 0); int entity_ed = BinaryHelper.SequenceIndexOf(temp, _boundary, entity_st + _boundary.Length + 2); if (isFile && !isFinished) { if (entity_st == 0) { int header_st = entity_st + _boundary.Length + 2; int header_ed = BinaryHelper.SequenceIndexOf(temp, _doubleBackSpace, header_st); int body_st = header_ed + 4; if (entity_ed == -1) { AppendToLastEntity(BinaryHelper.SubData(draft, body_st)); draft = null; } else { AppendToLastEntity(BinaryHelper.SubData(draft, body_st, entity_ed - body_st - 2)); isFinished = true; isFile = false; draft = BinaryHelper.SubData(draft, entity_ed); } } else { AppendToLastEntity(draft); draft = null; } } // When need append new data, combine the two // binary array into one. data = BinaryHelper.Combine(draft, data); draft = null; } while (true) { // find the boundary int entity_st = BinaryHelper.SequenceIndexOf(data, _boundary, 0); // if the current loaded data contain the boundary if (entity_st > -1) { if (isFile && !isFinished) { AppendToLastEntity(BinaryHelper.SubData(data, 0, entity_st - 2)); data = BinaryHelper.SubData(data, entity_st); isFile = false; isFinished = true; continue; } int entity_ed = BinaryHelper.SequenceIndexOf(data, _boundary, entity_st + _boundary.Length + 2); int header_st = entity_st + _boundary.Length + 2; int header_ed = BinaryHelper.SequenceIndexOf(data, _doubleBackSpace, header_st); int body_st = header_ed + 4; if (body_st < 4) { // If the header in the entity is not complete, then // set the draft as the data, and dump out the function // to ask for more data. draft = data; return; } else { // If the header in the entity is complete if (!isFile && isFinished) { // Encoding the data in the header of the entity string headerInEntity = ASCIIEncoding.UTF8.GetString( BinaryHelper.SubData(data, header_st, header_ed - header_st)); // If it is a file entity, the header contain the keyword:"filename". if (headerInEntity.IndexOf("filename") > -1) { // Use Regular Expression to get the meta key values from // the header of the entity. Regex detailsReg = new Regex("Content-Disposition: form-data; name=\"([^\"]*)\";" + " filename=\"([^\"]*)\"Content-Type: ([^\"]*)"); Match regMatch = detailsReg.Match(headerInEntity.Replace("\r\n", "")); string controlName = regMatch.Groups[1].Value; string clientPath = regMatch.Groups[2].Value; string contentType = regMatch.Groups[3].Value; if (string.IsNullOrEmpty(clientPath)) { isFile = false; } else { isFile = true; // Create a new instance for the file entity UploadFile up = new UploadFile(clientPath, contentType); FilterResult.Add(up); isFinished = false; } } else { isFile = false; } } } if (entity_ed > -1) { // If we can find another boundary after the first boundary, // that means the entity block is ended there. // Only if it is a file entity we need to get the data // in the body of the entity if (isFile) { AppendToLastEntity(BinaryHelper.SubData(data, body_st, entity_ed - body_st - 2)); isFinished = true; isFile = false; } // Remove the current processed entity data // and loop for the next one. data = BinaryHelper.SubData(data, entity_ed); if (BinaryHelper.Equals(data, _endTag)) { data = null; draft = null; return; } continue; } else { // If we can't find the end mark, we have to // move the data to the draft and // ask for new data to append. draft = data; return; } } else { // If we can't find any mark of the boundary, // we have to move the data to the draft and // ask for new data to append. draft = data; return; } } }
// 过滤请求数据来获取文件的二进制数据. // 这里是过滤数据的算法逻辑. // 在我们讲到算法之前将会有很多内容要 // 讲(可能要一本书).所以我只在这里开 // 个头,但是忽略详细的解释. private void FilterFileDataFromBodyEntity(byte[] data) { if (data == null) { return; } if (draft != null) { byte[] temp = BinaryHelper.Combine(draft, BinaryHelper.Copy(data, 0, _boundary.Length)); int entity_st = BinaryHelper.SequenceIndexOf(temp, _boundary, 0); int entity_ed = BinaryHelper.SequenceIndexOf(temp, _boundary, entity_st + _boundary.Length + 2); if (isFile && !isFinished) { if (entity_st == 0) { int header_st = entity_st + _boundary.Length + 2; int header_ed = BinaryHelper.SequenceIndexOf(temp, _doubleBackSpace, header_st); int body_st = header_ed + 4; if (entity_ed == -1) { AppendToLastEntity(BinaryHelper.SubData(draft, body_st)); draft = null; } else { AppendToLastEntity(BinaryHelper.SubData(draft, body_st, entity_ed - body_st - 2)); isFinished = true; isFile = false; draft = BinaryHelper.SubData(draft, entity_ed); } } else { AppendToLastEntity(draft); draft = null; } } // 当需要添加新数据时, // 把这两个二进制数组合并成一个. data = BinaryHelper.Combine(draft, data); draft = null; } while (true) { // 找到边界 int entity_st = BinaryHelper.SequenceIndexOf(data, _boundary, 0); // 如果当前加载的数据包含边界 if (entity_st > -1) { if (isFile && !isFinished) { AppendToLastEntity(BinaryHelper.SubData(data, 0, entity_st - 2)); data = BinaryHelper.SubData(data, entity_st); isFile = false; isFinished = true; continue; } int entity_ed = BinaryHelper.SequenceIndexOf(data, _boundary, entity_st + _boundary.Length + 2); int header_st = entity_st + _boundary.Length + 2; int header_ed = BinaryHelper.SequenceIndexOf(data, _doubleBackSpace, header_st); int body_st = header_ed + 4; if (body_st < 4) { // 如果实体的头部不完整, // 设置这个实体作为数据, // 卸载掉函数来请求更多的数据. draft = data; return; } else { // 如果实体的头部完整 if (!isFile && isFinished) { // 把实体头部的数据转换为UTF8编码 string headerInEntity = ASCIIEncoding.UTF8.GetString( BinaryHelper.SubData(data, header_st, header_ed - header_st)); // 如果这是一个文件实体,头部包会含关键字:"filename". if (headerInEntity.IndexOf("filename") > -1) { // 在实体的头部使用正则表达式来 // 获取元数据的关键字值. Regex detailsReg = new Regex("Content-Disposition: form-data; name=\"([^\"]*)\";" + " filename=\"([^\"]*)\"Content-Type: ([^\"]*)"); Match regMatch = detailsReg.Match(headerInEntity.Replace("\r\n", "")); string controlName = regMatch.Groups[1].Value; string clientPath = regMatch.Groups[2].Value; string contentType = regMatch.Groups[3].Value; if (string.IsNullOrEmpty(clientPath)) { isFile = false; } else { isFile = true; // 为文件实体创建一个新的实例 UploadFile up = new UploadFile(clientPath, contentType); FilterResult.Add(up); isFinished = false; } } else { isFile = false; } } } if (entity_ed > -1) { // 如果我们在第一个边界之后又发现另一边界, // 那表示实体块在那里结束了. // 只是文件实体的时候,我们才需要在实体的 // 主体里获取数据. if (isFile) { AppendToLastEntity(BinaryHelper.SubData(data, body_st, entity_ed - body_st - 2)); isFinished = true; isFile = false; } // 移除当前的实体处理数据并且 // 循环到下一个数据. data = BinaryHelper.SubData(data, entity_ed); if (BinaryHelper.Equals(data, _endTag)) { data = null; draft = null; return; } continue; } else { // 如果我们不能找到结束标记,我们 // 必须要把这些数据移到草稿并 // 请求添加新数据. draft = data; return; } } else { // 如果我们不能找到结束标记,我们 // 必须要把这些数据移到草稿并 // 请求添加新数据. draft = data; return; } } }
private void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; HttpContext context = app.Context; // We need the HttpWorkerRequest of the current context to // process the request data. For more details about HttpWorkerRequest, // please follow the Readme file in the root directory. IServiceProvider provider = (IServiceProvider)context; System.Web.HttpWorkerRequest request = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest)); // Get the content type of the current request. string contentType = request.GetKnownRequestHeader( System.Web.HttpWorkerRequest.HeaderContentType); // If we could not get the content type, then skip out the module if (contentType == null) { return; } // If the content type is not multipart/form-data, // means that there is no file upload request // then skip out the moudle if (contentType.IndexOf("multipart/form-data") == -1) { return; } string boundary = contentType.Substring(contentType.IndexOf("boundary=") + 9); // Get the content length of the current request long contentLength = Convert.ToInt64( request.GetKnownRequestHeader( HttpWorkerRequest.HeaderContentLength)); // Get the data of the portion of the HTTP request body // that has currently been read. // This is the first step for us to store the upload file. byte[] data = request.GetPreloadedEntityBody(); // Create an instance of the manager class which // help to filter the request data. FileUploadDataManager storeManager = new FileUploadDataManager(boundary); // Append the preloaded data. storeManager.AppendData(data); UploadStatus status = null; if (context.Cache[_cacheContainer] == null) { //Initialize the UploadStatus which used to //store the status for the client. status = new UploadStatus( context, // Send the current context to the status // which will be used for the events. contentLength // Initialize the file length. ); // Bind a event when update the status. status.OnDataChanged += new UploadStatusEventHandler(status_OnDataChanged); } else { status = context.Cache[_cacheContainer] as UploadStatus; if (status.IsFinished) { return; } } // Set the first read data length to the status class. if (data != null) { status.UpdateLoadedLength(data.Length); } // Get the length of the left request data. long leftdata = status.ContentLength - status.LoadedLength; // Define a custom buffer length int customBufferLength = Convert.ToInt32(Math.Ceiling((double)contentLength / 16)); if (customBufferLength < 1024) { customBufferLength = 1024; } while (!request.IsEntireEntityBodyIsPreloaded() && leftdata > 0) { // Check if user abort the upload, then close the connection if (status.Aborted) { // Delete the cached files. foreach (UploadFile file in storeManager.FilterResult) { file.ClearCache(); } request.CloseConnection(); return; } // If the length the remained request data // is less than the buffer length, // then set the buffer length as the remained data length. if (leftdata < customBufferLength) { customBufferLength = (int)leftdata; } // Read a custom buffer length of the request data data = new byte[customBufferLength]; int redlen = request.ReadEntityBody(data, customBufferLength); if (customBufferLength > redlen) { data = BinaryHelper.SubData(data, 0, redlen); } // Append the left data. storeManager.AppendData(data); // Add the buffer length to the status to update the upload status status.UpdateLoadedLength(redlen); leftdata -= redlen; } // After all the data has been read, // save the uploaded files. foreach (UploadFile file in storeManager.FilterResult) { file.Save(null); } }
private void context_BeginRequest(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; HttpContext context = app.Context; // 我们需要HttpWorkerRequest的当前内容来处理请求数据. // 要想知道HttpWorkerRequest更多更详细的内容, // 请参考根目录下的Readme文件. IServiceProvider provider = (IServiceProvider)context; System.Web.HttpWorkerRequest request = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest)); // 获取当前请求的内容类型. string contentType = request.GetKnownRequestHeader( System.Web.HttpWorkerRequest.HeaderContentType); // 如果我们不能获取内容类型,跳过这个模块. if (contentType == null) { return; } // 如果内容类型不是multipart/form-data, // 意味着没有上传请求, // 就可以跳过这个模块. if (contentType.IndexOf("multipart/form-data") == -1) { return; } string boundary = contentType.Substring(contentType.IndexOf("boundary=") + 9); // 获取当前请求的内容长度. long contentLength = Convert.ToInt64( request.GetKnownRequestHeader( HttpWorkerRequest.HeaderContentLength)); // 获取HTTP请求主体的那些 // 当前已经被读取的数据. // 这是我们存储上传文件的第一步. byte[] data = request.GetPreloadedEntityBody(); // 创建一个管理类的实例可以 // 帮助过滤请求数据. FileUploadDataManager storeManager = new FileUploadDataManager(boundary); // 添加预装载的数据. storeManager.AppendData(data); UploadStatus status = null; if (context.Cache[_cacheContainer] == null) { //初始化UploadStatus, //它被用来存储客户状态. status = new UploadStatus( context, // 把当前内容发送到status被事件使用 // contentLength // 初始化文件长度. ); // 当更新状态时绑定事件. status.OnDataChanged += new UploadStatusEventHandler(status_OnDataChanged); } else { status = context.Cache[_cacheContainer] as UploadStatus; if (status.IsFinished) { return; } } // 把首先读到的数据长度设置到status class. if (data != null) { status.UpdateLoadedLength(data.Length); } // 获取留下的请求数据的长度. long leftdata = status.ContentLength - status.LoadedLength; // 定义一个自定义的缓存区的长度 int customBufferLength = Convert.ToInt32(Math.Ceiling((double)contentLength / 16)); if (customBufferLength < 1024) { customBufferLength = 1024; } while (!request.IsEntireEntityBodyIsPreloaded() && leftdata > 0) { // 检查用户如果终止了上传,关闭连接. if (status.Aborted) { // 删除缓存文件. foreach (UploadFile file in storeManager.FilterResult) { file.ClearCache(); } request.CloseConnection(); return; } // 如果剩下的请求数据小于缓 // 冲区的长度,把缓冲区的 // 长度设置成剩余数据的长度. if (leftdata < customBufferLength) { customBufferLength = (int)leftdata; } // 读取自定义缓冲区的长度的请求数据 data = new byte[customBufferLength]; int redlen = request.ReadEntityBody(data, customBufferLength); if (customBufferLength > redlen) { data = BinaryHelper.SubData(data, 0, redlen); } // 添加剩余数据. storeManager.AppendData(data); // 把缓冲区的长度添加到status来更新上传status. status.UpdateLoadedLength(redlen); leftdata -= redlen; } // 当所有的数据都被读取之后, // 保存上传文件. foreach (UploadFile file in storeManager.FilterResult) { file.Save(null); } }