[转]Asp.Net 上传大文件专题(3)--从请求流中获取数据并保存为文件[下]
转自:http://www.cnblogs.com/stg609/archive/2008/08/03/1259206.html
接着上一篇讲
3.4 读取剩余的请求
前面我们已经提到过ReadEntityBody (Byte[] buffer, Int32 size)方法,该方法可以用来读取客户端的请求数据。我们想要读取剩余部分的请求数据,就是要使用这个方法来从异名管道中循环取出请求。 [buffer:将数据读入的字节数组;size:最多读取的字节数;如果所被读取的剩余请求字节长度小于size,那么该方法会将多余的size大小的字节数组用0填充,这样会损失不必要的性能,因此我们在使用该方法前最好先判断下剩余的HTTP请求大小与size的关系。据其他前辈们测试该方法大多数读取的数据长度都在8192左右,所以size不必定的很大。]
while (iLeave > iReadStepSize && request.IsClientConnected())
{//首先判断剩余的请求大小是否大于iReadStepSize
iRead = request.ReadEntityBody(bReadStepByte, iReadStepSize);
/**//*读取最大字节数为iReadStepSize的用户请求到bReadStepByte数组中*/
对文件内容的处理
iLeave -= iRead;
}
if (iLeave > 0 && request.IsClientConnected())
{
iRead = request.ReadEntityBody(bReadStepByte, iLeave);
/**//*最后还剩一部分,因为小于iReadStepSize,所以写在循环外*/
对文件内容的处理
}
3.5 从请求中截取上传数据,并将除去文件数据后的请求写入缓存
因为文件上传的特殊编码方式采用分隔符来分隔不同的内容,所以我们只要利用分隔符便能精确的获取文件数据所在的区间,然后将其中的数据截取。看起来是不是很简单呢?
我们先来看一下文件上传的HTTP请求内容的实际存在形式(因为内容比较多,我选取其中一部分,其中的序号是为了方便说明加上去的):
02 Content-Disposition: form-data; name="tbVideoName"
03
04 vnm
05 -----------------------------7d87d1cc0a88
06 Content-Disposition: form-data; name="file"; filename="C:\Documents and Settings\stg609\妗岄潰\浣冲彞.txt"
07 Content-Type: text/plain
08
09 这里是我上传的文本内容
10 -----------------------------7d87d1cc0a88
11 Content-Disposition: form-data; name="__EVENTVALIDATION"
12
13 /wEWBgK0g/7JCQLrqYKOBgKj5pr/CAKBmOPQBQLY14yNBQKmmtpNX1cOVXyqN8xEER3ZXbnXzsUwVVo=
14 -----------------------------7d87d1cc0a88--
由上面的内容我们可以发现(1)分隔符为“-----------------------------7d87d1cc0a88”,其中的数字是随机的,并不固定,但是在同一个请求中都是相同的;(2)02、06、07、11等类似信息被称谓实体头,我们可以发现一个请求流中包含了所有控件(包括隐藏域);(3)实体内容与实体头之前用一个空行分开,也就是说有两个换行符,如02和04、07和09等;(4)最后一个内容的分隔符较一般的分隔符多出"--"。另外也可以发现,如果是文件上传,则实体头内会有"filename=文件路径"的信息。
我们要做的便是将上述请求的09行的内容截取出来,而其它不变。但是要完整的截取文件数据并没有这么简单,而且还不止一个文件。因此我们要考虑很多因素,之前看过其它人的代码,感觉比较简短,但是似乎没有完全考虑到某些因素。
我认为考虑因素应该包括如下几点[可能考虑的并不全面,或者考虑的太多,希望大家多提意见]:
a、因为我们以"filename="字符串为标识查找文件开始的位置,所以要判断该字符串是否为于两个数据流中
b、"filename="之后开始到这之后的第一个换行符之前是文件名,所以为了正确获取文件名,要判断这之后的换行符是否与"filename="在同一个数据流内
c、这之后是表示文件类型,在其后又有两个换行符,然后才是真正的文件数据首位置。为获取正确的开始位置,要判断两个换行符是否与"filename="在一个数据流内
d、当开始读取数据,我们要知道什么时候文件结束,所以要查找分隔符,所以要确保分隔符在一个数据流内
e、我们还要知道什么时候已经结束整个请求,所以要查找结束分隔符,因此要判断结束分隔符是否在一个数据流内
至于如何确保标识符在一个数据流内,参考了一些前辈的做法。一般就是利用临时数组对可能分隔于两个数据流内的标识符进行保存。然后拼接在下一次数据流之前与下一次数据流一起做为整体进行处理。
为了便于理解,我自己搞了个图,大家看看:
通过上面这图,大家应该对整体的流程有了一定的认识,我们在编码中只需要按照上面的流程编码就基本上可以保证上传文件数据的完整性。
我就不讲解全部代码了,到时候全系列写完之后,提供大家下载。要讲的主要有如下几点:
1)如何获取分隔符
通过reflector反编译System.Web.HttpRequest可以找到GetMultipartBoundary()方法,这便是用来查找分隔符用的。代码如下:
private byte[] GetMultipartBoundary()
{
string attributeFromHeader = GetAttributeFromHeader(this.ContentType, "boundary");
if (attributeFromHeader == null)
{
return null;
}
attributeFromHeader = "--" + attributeFromHeader;
return Encoding.ASCII.GetBytes(attributeFromHeader.ToCharArray());
}
2)如何知道文件数据开始
由HTTP请求内容中可以看到,文件开始处数据与实体头之前差距两个换行符,所以我们首先找到“filename=”,然后找到“Content-Type: ”,最后找到两个换行符的末尾,这便是文件开始处位置
3)如何将数据写入文件
首先根据文件名,我们可以在文件数据开始前创建一个文件。然后通过FileStream这个I/O流的Write方法将字节数组写入之前创建的文件。 [注意使用完FileStream必须将它关闭,否则所写入的文件一直处于被占用的情况,那其它程序将无法使用]
//以上是创建一个文件
fs.Write(FileByte, FileStartPos, FileReadedLength);
//以上是将读取到的部分文件数据写入该文件
4)如何保留除去文件数据的请求
这个其实和我们读取文件数据正好相反,因为文件数据的读取实际上就是将文件数据从起始处开始保存一直到文件数据结束。而除去文件数据的请求则是从请求的第一个数据开始保存,当文件数据开始后,则不保存,直到文件数据结束后,又继续保存。因为除去文件数据后的请求比较小,所以我们可以直接用一个字节数组进行保存。
/**//// <summary>
/// 将除去文件数据的其余HTTP请求写入缓存
/// </summary>
/// <param name="bWriteByte">要被写入缓存的数组</param>
/// <param name="iStartIndex">被写入数组的起始位置</param>
/// <param name="iLength">被写入数组的长度</param>
/// <param name="bHttpRequestByte">HTTP缓存</param>
private void WriteHttpRequestWithoutFileData(byte[] bWriteByte,int iStartIndex, int iLength,ref byte[] bHttpRequestByte)
{
if (bHttpRequestByte == null)
{
bHttpRequestByte = new byte[iLength];
Array.Copy(bWriteByte, iStartIndex, bHttpRequestByte, 0, iLength);//将文件数据之前的所有内容放入缓存用于稍后封装HTTP请求
}
else
{
byte[] newbyte = new byte[iLength];
Array.Copy(bWriteByte, iStartIndex, newbyte, 0, iLength);
bHttpRequestByte = UnionByte(newbyte, bHttpRequestByte);
}
}
private byte[] UnionByte(byte[] bnewbyte, byte[] boldbyte)
{
int iLen = 0;
if (bnewbyte != null)
{//要添加到原数组中的字节如果不为空
if (boldbyte != null)
{
iLen = boldbyte.Length;
}
byte[] unionbyte = new byte[bnewbyte.Length + iLen];
if (boldbyte != null)
{
boldbyte.CopyTo(unionbyte, 0);
}
bnewbyte.CopyTo(unionbyte, iLen);
return unionbyte;
}
else
{
return boldbyte;
}
}
3.6 重新封装HTTP请求
这部分要利用反射实现,由于本人对反射这块还不是很熟悉,所以代码借鉴高山来客在Asp.NET大文件上传组件开发总结(四)---封送数据给Asp.NET页面中的代码:
private void InjectTextParts(HttpRequest request, byte[] textParts)
{
BindingFlags flags1 = BindingFlags.NonPublic | BindingFlags.Instance;
Type type1 = request.GetType();
FieldInfo info1 = type1.GetField("_rawContent", flags1);
FieldInfo info2 = type1.GetField("_contentLength", flags1);
if ((info1 != null) && (info2 != null))
{
Assembly web = Assembly.GetAssembly(typeof(HttpRequest));
Type hraw = web.GetType("System.Web.HttpRawUploadedContent");
object[] argList = new object[2];
argList[0] = textParts.Length + 1024;
argList[1] = textParts.Length;
CultureInfo currCulture = CultureInfo.CurrentCulture;
object httpRawUploadedContent = Activator.CreateInstance(hraw,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
argList,
currCulture,
null);
Type contentType = httpRawUploadedContent.GetType();
contentType.GetField("_completed", flags1).SetValue(httpRawUploadedContent, true);//不加上这句就会有问题
FieldInfo dataField = contentType.GetField("_data", flags1);
dataField.SetValue(httpRawUploadedContent, textParts);
FieldInfo lengthField = contentType.GetField("_length", flags1);
lengthField.SetValue(httpRawUploadedContent, textParts.Length);
FieldInfo fileThresholdField = contentType.GetField("_fileThreshold", flags1);
fileThresholdField.SetValue(httpRawUploadedContent, textParts.Length + 1024);
info1.SetValue(request, httpRawUploadedContent);
info2.SetValue(request, textParts.Length);
}
}
4 在aspx页面中加入一个<input type="file" id="FileUpload1" name="file" />,并修改Form的enctype = " multipart/form-data " , 测试一下吧
好了,到这差不多就基本完成了,但是这样只是实现了文件的上传,并没有发挥HTTP模块真正的魅力。接下来一篇将向大家介绍如何实现进度条的显示。
下一篇
[转]Asp.Net 上传大文件专题(3)--从请求流中获取数据并保存为文件[下]相关推荐
- Asp.Net 上传大文件专题(3)--从请求流中获取数据并保存为文件[下]
接着上一篇讲 3.4 读取剩余的请求 前面我们已经提到过ReadEntityBody (Byte[] buffer, Int32 size)方法,该方法可以用来读取客户端的请求数据 ...
- Asp.Net 上传大文件专题(4)--利用ajax技术显示上传进度
====================================================== 注:本文源代码点此下载 ================================= ...
- asp.net 上传大文件控件
这段时间写了个asp.net 上传大文件控件.经过测试,在ie中可显示进度条.特此共享,望广大网友多提意见. 大文件上传控件(包含进度条) 使用说明如下: <summary> 上 ...
- 用ASP.NET上传大文件
作者:思归 微软MVP http://blog.joycode.com/saucer/ 我们在上传大文件时都遇到过这样或那样的问题.设置很大的maxRequestLength值并不能完全解 ...
- asp.net 上传大文件解决方案(转)
这次在项目中,用到了大文件上传,要上传的文件有100多m,于是研究现在国内使用的大文件上传的 组件发现用的比较多的有两个控件AspnetUpload 2.0和Lion.Web.UpLoadModule ...
- ASP无法上传大文件的解决方法
一.错误提示--ASP 0104 : 80004005 (1)ASP 0104 : 80004005 问题:WIN2003无法上传较大的文件"Request 对象 错误 \'ASP 0104 ...
- Asp.Net 上传大文件
HttpModule.cs 代码 namespace WebUploadFile { public class HttpUploadModule : IHttpModule { ...
- csv文件示例_如何在R中使用数据框和CSV文件-带有示例的详细介绍
csv文件示例 Welcome! If you want to start diving into data science and statistics, then data frames, CSV ...
- oracle asp.net上传 下载 文件大小只有32kb,asp默认上传大小
asp.net 上传大文件大小控制方案 .NET默认允许上传4M的文件. 修改 Machine.config 文件,改动MaxRequestLength 参数的值.在c:\winnt\microsof ...
最新文章
- 为什么TCP的TIME_WAIT状态要保持2MSL?
- 3.1常用类(java学习笔记)包装类及日期类
- 【arduino】亲测MAC上arduino安装ESP32 SPIFFS插件
- 怎么在linux指定目录下查找文件夹下,Linux下如何使用find命令指定目录查找文件...
- C# MVC的博客开发(二)登录
- android 动态设置textview的边距,添加边距为动态的Android的TextView(Adding Margins to a dynam...
- ten sentences(1-10)
- Python的作用是什么
- 开发了个 Flipper 调试工具的 Flutter 版本 SDK,让 Flutter 应用调试起来更容易 1
- wingdings字体符号在哪_出版社编辑对标点符号和专业术语的要求
- 原型和原型链的理解(通俗易懂)
- win10下LPT并口打印失败和POS打印机的钱箱不能打开,win10的坑
- 使用系统文件检查器工具修复丢失或损坏的系统文件
- 光栅图形学-中点画线法
- python 培训线下
- 单片机led灯闪烁实验总结_这样处理家中LED灯和节能灯关了还是微亮或闪烁的故障...
- Java实现 蓝桥杯 算法训练 递归求二项式系数
- 解决Hibernate:could not initialize proxy - no Session
- mysql:查询排名
- 全国计算机自考应用题,近几年度自考管理系统中计算机硬应用题汇总.doc