IT貓撲網(wǎng):您身邊最放心的安全下載站! 最新更新|軟件分類|軟件專題|手機版|論壇轉(zhuǎn)貼|軟件發(fā)布

您當前所在位置: 首頁網(wǎng)絡編程.Net編程 → 在ASP.NET MVC中實現(xiàn)大文件異步上傳

在ASP.NET MVC中實現(xiàn)大文件異步上傳

時間:2015-06-28 00:00:00 來源:IT貓撲網(wǎng) 作者:網(wǎng)管聯(lián)盟 我要評論(0)

  在ASP.NET中通過HTTP上傳大文件是一個由來已久的挑戰(zhàn),它是許多活躍的ASP.NET論壇最常討論的問題之一,除了處理大文件外,用戶還經(jīng)常被要求要顯示出文件上傳的進度,當你需要直接控制從瀏覽器上傳數(shù)據(jù)流時,你會四處碰壁。51CTO.com之前就曾針對性的報道過《解除ASP.NET上傳文件的大小限制》和《ASP.NET大文件上傳開發(fā)總結(jié)》等文章。

  絕大多數(shù)人認為在ASP.NET中上傳大文件有以下這些解決方案:

  ◆不要這樣做。你最好是在頁面中嵌入一個Silverlight或Flash進程上傳文件。

  ◆不要這樣做。因為HTTP本身設(shè)計就不是為了上傳大文件,重新思考你要的功能。

  ◆不要這樣做。ASP.NET本身設(shè)計最大也就能處理2GB大小的文件。

  ◆購買商業(yè)產(chǎn)品,如SlickUpload,它使用了一個HttpModule實現(xiàn)了文件流分塊。

  ◆使用開源產(chǎn)品,如NeatUpload,它使用了一個HttpModule實現(xiàn)了文件流分塊。

  最近我接到一個任務,需構(gòu)建一個上傳工具實現(xiàn)以下功能:

  ◆必須工作在HTTP協(xié)議

  ◆必須允許非常大的文件上傳(會大于2GB)

  ◆必須允許斷點續(xù)傳

  ◆必須允許并行上傳

  因此前三個解決方案都不適應我的需求,其它解決方案對于我而言又太笨重了,因此我開始著手解決在ASP.NET MVC中的這個問題,如果有這方面的開發(fā)背景,你一定了解大部分問題最終都歸結(jié)于對ASP.NET輸入流和連鎖請求過程的控制,網(wǎng)上的資料一般都是這樣描述的,只要你的代碼訪問了HttpRequest的InputStream屬性,在你訪問流之前,ASP.NET就會緩存整個上傳的文件,這就意味著當我向云服務上傳文件時,我必須等待整個大文件抵達服務器,然后才能將其傳輸?shù)筋A定目的地,這意味著需要兩倍的時間。

  首先,我們推薦你閱讀一下Scott Hanselman的有關(guān)ASP.NET MVC文件上傳文章,地址http://www.hanselman.com/blog/CommentView.aspx?guid=bc137b6b-d8d0-47d1-9795-f8814f7d1903,先對文件上傳有一個大致的了解,但Scott Hanselman的方法是不能上傳大文件的,根據(jù)Scott Hanselman的方法,你只需要修改一下web.config文件,確保ASP.NET允許最大支持2GB大小的文件上傳,不要擔心,這樣設(shè)置并不會吃掉你的內(nèi)存,因為凡是大于256KB的數(shù)據(jù)都被緩存到磁盤上去了。

  ﹤system.web﹥? ﹤httpruntime requestlengthdiskthreshold="256" maxrequestlength="2097151"﹥? ﹤/httpruntime﹥﹤/system.web﹥ 這是一個簡單的適合大多數(shù)應用的解決辦法,但我的任務中不能借用這種方法,即使會將數(shù)據(jù)緩存到磁盤中,但這種類似于另存為的方法也會使用大量的內(nèi)存。

  圖 1 :通過緩存整個文件,然后另存為的方式會使內(nèi)存消耗突然上升

  那么在ASP.NET MVC中通過直接訪問流,不觸發(fā)任何緩存機制,上傳大文件該如何實現(xiàn)呢?解決辦法就是盡量遠離ASP.NET,我們先來看一看UploadController,它有三個行為方法,一個是索引我們上傳的文件,一個是前面討論的緩存邏輯,另一個是基于實時流的方法。

  public class UploadController : Controller

  {

  [AcceptVerbs(HttpVerbs.Get)]

  [Authorize]

  public ActionResult Index()

  {

  return View();

  }

  [AcceptVerbs(HttpVerbs.Post)]

  public ActionResult BufferToDisk()

  {

  var path = Server.MapPath("~/Uploads");

  foreach (string file in Request.Files)

  {

  var fileBase = Request.Files[file];

  try {

  if (fileBase.ContentLength > 0)

  {

  fileBase.SaveAs(Path.Combine(path, fileBase.FileName));

  }

  }

  catch (IOException)

  {

  }

  }

  return RedirectToAction("Index", "Upload");

  }

  //[AcceptVerbs(HttpVerbs.Post)]

  //[Authorize]

  public void LiveStream()

  {

  var path = Server.MapPath("~/Uploads");

  var context = ControllerContext.HttpContext;

  var provider = (IServiceProvider)context;

  var workerRequest = (HttpWorkerRequest)provider.GetService(typeof(HttpWorkerRequest));

  //[AcceptVerbs(HttpVerbs.Post)]

  var verb = workerRequest.GetHttpVerbName();

  if(!verb.Equals("POST"))

  {

  Response.StatusCode = (int)HttpStatusCode.NotFound;

  Response.SuppressContent = true;

  return;

  }

  //[Authorize]

  if(!context.User.Identity.IsAuthenticated)

  {

  Response.StatusCode = (int)HttpStatusCode.Unauthorized;

  Response.SuppressContent = true;

  return;

  }

  var encoding = context.Request.ContentEncoding;

  var processor = new UploadProcessor(workerRequest);

  processor.StreamToDisk(context, encoding, path);

  //return RedirectToAction("Index", "Upload");

  Response.Redirect(Url.Action("Index", "Upload"));

  }

  }

  雖然這里明顯缺少一兩個類,但基本的方法還是講清楚了,看起來和緩存邏輯并沒有太大的不同之處,我們?nèi)匀粚⒘骶彺娴搅舜疟P,但具體處理方式卻有些不同了,首先,沒有與方法關(guān)聯(lián)的屬性,謂詞和授權(quán)限制都被移除了,使用手動等值取代了,使用手工響應操作而不用ActionFilterAttribute聲明的原因是這些屬性涉及到了一些重要的ASP.NET管道代碼,實際上在我的代碼中,我還特意攔截了原生態(tài)的HttpWorkerRequest,因為它不能同時做兩件事情。

  HttpWorkerRequest有VIP訪問傳入的請求,通常它是由ASP.NET本身支持工作的,但我們綁架了請求,然后欺騙剩下的請求,讓它們誤以為前面的請求已經(jīng)全部得到處理,為了做到這一點,我們需要上面例子中未出現(xiàn)的UploadProcessor類,這個類的職責是物理讀取來自瀏覽器的每個數(shù)據(jù)塊,然后將其保存到磁盤上,因為上傳的內(nèi)容被分解成多個部分,UploadProcessor類需要找出內(nèi)容頭,然后拼接成帶狀數(shù)據(jù)輸出,這一可以在一個上傳中同時上傳多個文件。

  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);

  }

關(guān)鍵詞標簽:ASP.NET

相關(guān)閱讀

文章評論
發(fā)表評論

熱門文章 誅仙3飛升任務怎么做-誅仙3飛升任務流程最新2022 誅仙3飛升任務怎么做-誅仙3飛升任務流程最新2022 鐘離圣遺物推薦-原神鐘離圣遺物詞條 鐘離圣遺物推薦-原神鐘離圣遺物詞條 解決方法:應用程序“DEFAULT WEB SITE”中的服務器錯誤 解決方法:應用程序“DEFAULT WEB SITE”中的服務器錯誤 使用aspnet_regiis.exe 重新注冊.NET Framework 使用aspnet_regiis.exe 重新注冊.NET Framework

相關(guān)下載

    人氣排行 誅仙3飛升任務怎么做-誅仙3飛升任務流程最新2022 asp.net表單提交方法GET\POST 在ASP.NET中如何判斷用戶IE瀏覽器的版本 Asp.net中messagebox的實現(xiàn)方法 Asp.net中的web.config配置 在ASP.NET MVC中實現(xiàn)大文件異步上傳 asp.net獲取URL和IP地址 FileUpload上傳多文件出現(xiàn)錯誤的解決方法