在ASP.NET MVC中实现大文件异步上传(2)
来源:互联网 发布:linux防火墙查看命令 编辑:程序博客网 时间:2024/06/05 21:04
HttpWorkerRequest有VIP访问传入的请求,通常它是由ASP.NET本身支持工作的,但我们绑架了请求,然后欺骗剩下的请求,让它们误以为前面的请求已经全部得到处理,为了做到这一点,我们需要上面例子中未出现的UploadProcessor类,这个类的职责是物理读取来自浏览器的每个数据块,然后将其保存到磁盘上,因为上传的内容被分解成多个部分,UploadProcessor类需要找出内容头,然后拼接成带状数据输出,这一可以在一个上传中同时上传多个文件。
internal class UploadProcessor
{
private byte[] _buffer;
private byte[] _boundaryBytes;
private byte[] _endHeaderBytes;
private byte[] _endFileBytes;
private byte[] _lineBreakBytes;
private const string _lineBreak = "/r/n";
private readonly Regex _filename =
new Regex(@"Content-Disposition:/s*form-data/s*;/s*name/s*=/s*""file""/s*;/s*filename/s*=/s*""(.*)""",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
private readonly HttpWorkerRequest _workerRequest;
public UploadProcessor(HttpWorkerRequest workerRequest)
{
_workerRequest = workerRequest;
}
public void StreamToDisk(IServiceProvider provider, Encoding encoding, string rootPath)
{
var buffer = new byte[8192];
if (!_workerRequest.HasEntityBody())
{
return;
}
var total = _workerRequest.GetTotalEntityBodyLength();
var preloaded = _workerRequest.GetPreloadedEntityBodyLength();
var loaded = preloaded;
SetByteMarkers(_workerRequest, encoding);
var body = _workerRequest.GetPreloadedEntityBody();
if (body == null) // IE normally does not preload
{
body = new byte[8192];
preloaded = _workerRequest.ReadEntityBody(body, body.Length);
loaded = preloaded;
}
var text = encoding.GetString(body);
var fileName = _filename.Matches(text)[0].Groups[1].Value;
fileName = Path.GetFileName(fileName); // IE captures full user path; chop it
var path = Path.Combine(rootPath, fileName);
var files = new List {fileName};
var stream = new FileStream(path, FileMode.Create);
if (preloaded > 0)
{
stream = ProcessHeaders(body, stream, encoding, preloaded, files, rootPath);
}
// Used to force further processing (i.e. redirects) to avoid buffering the files again
var workerRequest = new StaticWorkerRequest(_workerRequest, body);
var field = HttpContext.Current.Request.GetType().GetField("_wr", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(HttpContext.Current.Request, workerRequest);
if (!_workerRequest.IsEntireEntityBodyIsPreloaded())
{
var received = preloaded;
while (total - received >= loaded && _workerRequest.IsClientConnected())
{
loaded = _workerRequest.ReadEntityBody(buffer, buffer.Length);
stream = ProcessHeaders(buffer, stream, encoding, loaded, files, rootPath);
received += loaded;
}
var remaining = total - received;
buffer = new byte[remaining];
loaded = _workerRequest.ReadEntityBody(buffer, remaining);
stream = ProcessHeaders(buffer, stream, encoding, loaded, files, rootPath);
}
stream.Flush();
stream.Close();
stream.Dispose();
}
private void SetByteMarkers(HttpWorkerRequest workerRequest, Encoding encoding)
{
var contentType = workerRequest.GetKnownRequestHeader(HttpWorkerRequest.HeaderContentType);
var bufferIndex = contentType.IndexOf("boundary=") + "boundary=".Length;
var boundary = String.Concat("--", contentType.Substring(bufferIndex));
_boundaryBytes = encoding.GetBytes(string.Concat(boundary, _lineBreak));
_endHeaderBytes = encoding.GetBytes(string.Concat(_lineBreak, _lineBreak));
_endFileBytes = encoding.GetBytes(string.Concat(_lineBreak, boundary, "--", _lineBreak));
_lineBreakBytes = encoding.GetBytes(string.Concat(_lineBreak + boundary + _lineBreak));
}
private FileStream ProcessHeaders(byte[] buffer, FileStream stream, Encoding encoding, int count, ICollection files, string rootPath)
{
buffer = AppendBuffer(buffer, count);
var startIndex = IndexOf(buffer, _boundaryBytes, 0);
if (startIndex != -1)
{
var endFileIndex = IndexOf(buffer, _endFileBytes, 0);
if (endFileIndex != -1)
{
var precedingBreakIndex = IndexOf(buffer, _lineBreakBytes, 0);
if (precedingBreakIndex > -1)
{
startIndex = precedingBreakIndex;
}
endFileIndex += _endFileBytes.Length;
var modified = SkipInput(buffer, startIndex, endFileIndex, ref count);
stream.Write(modified, 0, count);
}
else
{
var endHeaderIndex = IndexOf(buffer, _endHeaderBytes, 0);
if (endHeaderIndex != -1)
{
endHeaderIndex += _endHeaderBytes.Length;
var text = encoding.GetString(buffer);
var match = _filename.Match(text);
var fileName = match != null ? match.Groups[1].Value : null;
fileName = Path.GetFileName(fileName); // IE captures full user path; chop it
if (!string.IsNullOrEmpty(fileName) && !files.Contains(fileName))
{
files.Add(fileName);
var filePath = Path.Combine(rootPath, fileName);
stream = ProcessNextFile(stream, buffer, count, startIndex, endHeaderIndex, filePath);
}
else
{
var modified = SkipInput(buffer, startIndex, endHeaderIndex, ref count);
stream.Write(modified, 0, count);
}
}
else
{
_buffer = buffer;
}
}
}
else
{
stream.Write(buffer, 0, count);
}
return stream;
}
private static FileStream ProcessNextFile(FileStream stream, byte[] buffer, int count, int startIndex, int endIndex, string filePath)
{
var fullCount = count;
var endOfFile = SkipInput(buffer, startIndex, count, ref count);
stream.Write(endOfFile, 0, count);
stream.Flush();
stream.Close();
stream.Dispose();
stream = new FileStream(filePath, FileMode.Create);
var startOfFile = SkipInput(buffer, 0, endIndex, ref fullCount);
stream.Write(startOfFile, 0, fullCount);
return stream;
}
private static int IndexOf(byte[] array, IList value, int startIndex)
{
var index = 0;
var start = Array.IndexOf(array, value[0], startIndex);
if (start == -1)
{
return -1;
}
while ((start + index) < array.Length)
{
if (array[start + index] == value[index])
{
index++;
if (index == value.Count)
{
return start;
}
}
else
{
start = Array.IndexOf(array, value[0], start + index);
if (start != -1)
{
index = 0;
}
else
{
return -1;
}
}
}
return -1;
}
private static byte[] SkipInput(byte[] input, int startIndex, int endIndex, ref int count)
{
var range = endIndex - startIndex;
var size = count - range;
var modified = new byte[size];
var modifiedCount = 0;
for (var i = 0; i < input.Length; i++)
{
if (i >= startIndex && i < endIndex)
{
continue;
}
if (modifiedCount >= size)
{
break;
}
modified[modifiedCount] = input[i];
modifiedCount++;
}
input = modified;
count = modified.Length;
return input;
}
private byte[] AppendBuffer(byte[] buffer, int count)
{
var input = new byte[_buffer == null ? buffer.Length : _buffer.Length + count];
if (_buffer != null)
{
Buffer.BlockCopy(_buffer, 0, input, 0, _buffer.Length);
}
Buffer.BlockCopy(buffer, 0, input, _buffer == null ? 0 : _buffer.Length, count);
_buffer = null;
return input;
}
}
在处理代码的中间位置,你应该注意到了另一个类StaticWorkerRequest,这个类负责欺骗ASP.NET,在点击提交按钮时,它欺骗ASP.NET,让他认为没有文件上传,这是必需的,因为当上传完毕时,如果我们要重定向到所需的页面时,ASP.NET将会检查到在HTTP实体主体中仍然有数据,然后会尝试缓存整个上传,于是我们兜了一圈又回到了原点,为了避免这种情况,我们必须欺骗HttpWorkerRequest,将它注入到HttpContext中,获得请求开始部分的StaticWorkerRequest,它是唯一有用的数据。
internal class StaticWorkerRequest : HttpWorkerRequest
{
readonly HttpWorkerRequest _request;
private readonly byte[] _buffer;
public StaticWorkerRequest(HttpWorkerRequest request, byte[] buffer)
{
_request = request;
_buffer = buffer;
}
public override int ReadEntityBody(byte[] buffer, int size)
{
return 0;
}
public override int ReadEntityBody(byte[] buffer, int offset, int size)
{
return 0;
}
public override byte[] GetPreloadedEntityBody()
{
return _buffer;
}
public override int GetPreloadedEntityBody(byte[] buffer, int offset)
{
Buffer.BlockCopy(_buffer, 0, buffer, offset, _buffer.Length);
return _buffer.Length;
}
public override int GetPreloadedEntityBodyLength()
{
return _buffer.Length;
}
public override int GetTotalEntityBodyLength()
{
return _buffer.Length;
}
public override string GetKnownRequestHeader(int index)
{
return index == HeaderContentLength
? "0"
: _request.GetKnownRequestHeader(index);
}
// All other methods elided, they're just passthrough
}
使用StaticWorkerRequest建立虚假的声明,现在你可以在ASP.NET MVC中通过直接访问数据流上传大文件,使用这个代码作为开始,你可以很容易地保存过程数据,并使用Ajax调用另一个控制器行为展示其进度,将大文件缓存到一个临时区域,可以实现断点续传,不用再等待ASP.NET进程将整个文件缓存到磁盘上,同样,保存文件时也不用消耗另存为方法那么多的内存了。
- 在ASP.NET MVC中实现大文件异步上传
- 在ASP.NET MVC中实现大文件异步上传(2)
- 在ASP.NET MVC中实现大文件异步上传(2)
- 在ASP.NET MVC中实现大文件异步上传(2)
- 在ASP.NET MVC中实现大文件异步上传(1)
- 在.net MVC中异步上传图片或者文件
- ASP.NET MVC 大文件 分块上传
- ASP.NET实现异步上传文件
- 在ASP.NET中处理大文件上传
- 在ASP.NET中处理大文件上传
- InfoQ: 在ASP.NET中处理大文件上传
- 在ASP.NET中利用SlickUpload来上传大文件
- 在ASP.NET中处理大文件上传
- 在ASP.NET中处理大文件上传
- 在ASP.NET中处理大文件上传
- 如何在ASP.NET 应用中上传大文件
- ASP.NET MVC实现多文件上传
- 因位笔记之Asp.Net MVC异步上传文件
- 校园网中只有特定ip才能上网(未解决)
- Java中备份与还原Mysql
- 知识管理体系与信息优化
- [第一章]20110415-1.2.2.cpp : 如何把两个数加在一起
- 在ASP.NET MVC中实现大文件异步上传(2)
- 在ASP.NET MVC中实现大文件异步上传(2)
- install ibus on Ubuntu
- 在ASP.NET MVC中实现大文件异步上传(2)
- Ogre 实用技巧
- sharepoint 使用vs手动创建网站
- datagrid 头部垂直居中
- 五分钟教会五笔
- 使用c语言操作mysql
- 看帖的人开心一整天,回帖的人幸福一辈子!