ASP.Net异步HTTP文件上传处理程序 [英] ASP.Net Asynchronous HTTP File Upload Handler

查看:135
本文介绍了ASP.Net异步HTTP文件上传处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用C#创建一个异步的文件上载处理程序,并可以通过AJAX异步请求提供有关文件进度的更新.基本上,如果请求是POST,则它将一些信息加载到会话中,然后开始上载;如果请求是GET,则它将返回上载的当前状态(上载的字节数,总字节数等).我不太确定是否需要使用异步处理程序,但是文件可能很大,因此我认为效果最好.对于基本异步处理程序,我在本 MSDN文章.我已经在下面的代码的一些关键部分发布了内容.我遇到的问题是,在POST完成之前,我没有收到任何GET信息.我将提到,在此示例中,我将 jQuery 用于GET请求和

I'm trying to make a file upload handler in C# that is asynchronous and can provide updates on progress of the file through AJAX asynchronous requests. Basically if the request is a POST it loads some information into the session and then starts the upload, if the request was a GET it returns the current state of the upload (bytes uploaded, total bytes, etc). I'm not entire sure that it needs to be an asynchronous handler but the files could be quite large so I thought that would work best. For the base async handler I used something very similar to the handler in this MSDN article. I've posted below some key sections of my code below. The issue I'm having is that I don't receive any of the GET information back until the POST has completed. I will mention that in this example I am using jQuery for GET requests and BlueImp for posting the file.

HTML和JavaScript

The HTML and JavaScript

<input id="somefile" type="file" />

$(function () {
  name = 'MyUniqueId130';
  var int = null;
  $('#somefile').fileupload({
    url: '/fileupload.axd?key='+name,
    done: function (e, data) { clearInterval(int); }
  });

  $('#somefile').ajaxStart(function(){
    int = setInterval(function(){
    $.ajax({
      url: '/fileupload.axd?key='+name,
      dataType: 'json',
      async: true
    })
    .done(function(e1, data1){
      if(!e1.InProgress || e1.Complete || e1.Canceled)
        clearInterval(int);
    });
  }, 10000)});
});

无论是POST还是GET,异步过程请求方法都只是对以下方法之一调用正确的方法,然后调用CompleteRequest来结束请求:

The Asynchronous Process Request Method just calls the correct method whether it's a POST or GET to one of the following then calls CompleteRequest to end the request:

private static void GetFilesStatuses(HttpContext context)
{
  string key = context.Request.QueryString["key"];
  //A dictionary of <string, UploadStatus> in the session
  var Statuses = GetSessionStore(context);
  UploadStatus ups;

  if (!String.IsNullOrWhiteSpace(key))
  {
    if (Statuses.TryGetValue(key, out ups))
    {
      context.Response.StatusCode = (int)HttpStatusCode.OK;
      context.Response.Write(CreateJson(ups));
    }
    else
    {
      context.Response.StatusCode = (int)HttpStatusCode.NotFound;
    }
  }
  else
  {
    context.Response.StatusCode = (int)HttpStatusCode.OK;
    context.Response.Write(CreateJson(Statuses.Values));
  }
}

private static void UploadFile(HttpContext context)
{
 var Statuses = GetSessionStore(context);
 string key = context.Request.QueryString["key"];

 if (String.IsNullOrWhiteSpace(key))
 {
   context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
   return;
 }

 HttpPostedFile file = context.Request.Files[0];
 string extn = file.FileName.LastIndexOf('.') == -1 ? "" :
    file.FileName.Substring(file.FileName.LastIndexOf('.'), (file.FileName.Length - file.FileName.LastIndexOf('.')));
 string temp = GetTempFileName(path, extn);
 UploadStatus status = new UploadStatus()
 {
   FileName = file.FileName,
   TempFileName = temp,
   Path = path,
   Complete = false,
   Canceled = false,
   InProgress = false,
   Success = true,
   BytesLoaded = 0,
   TotalBytes = file.ContentLength
 };
 Statuses.Add(key, status);
 byte[] buffer = new byte[bufferSize];
 int byteCount = 0;

 using (var fStream = System.IO.File.OpenWrite(context.Request.MapPath(path + temp)))
 {
   uploads.Add(status);

   while ((byteCount = file.InputStream.Read(buffer, 0, bufferSize)) > 0 && !status.Canceled)
   {
     status.InProgress = true;
     status.BytesLoaded += byteCount;
     fStream.Write(buffer, 0, byteCount);
   }

   status.Complete = !status.Canceled;
   status.InProgress = false;
   status.Success = true;

   if (status.Canceled)
   {
     Statuses.Remove(temp);
   }

   context.Response.StatusCode = (int)HttpStatusCode.OK;
 }
}

我已经尝试了很多事情,例如非异步处理程序,异步处理程序,确保JavaScript正在运行异步,但是在这一点上,我认为我需要对这个问题有一些不同的看法,因此感谢任何人都可以提供的任何帮助

I've tried many things such as non-async handlers, async handlers, making sure the JavaScript is runnning async, but at this point I think I need some different eyes on the problem so thank you for any assistance anyone can provide.

推荐答案

我假定您正在使用默认的ASP.Net会话管理器,并且看到您调用GetSessionStore进行会话.不幸的是,当呼叫需要对会话存储的写访问权时,默认的会话管理器会序列化所有请求.这个 StackOverflow问题,并且此有关会话状态的MSDN弧形具有有关会话状态及其锁定行为的一些非常有用的信息.

I assume you're using the default ASP.Net Session manager and I see that you call GetSessionStore to get your session. Unfortunately the default Session manager serializes all requests when a call requires write access to the Session Store. This StackOverflow question and this MSDN arcle on Session State have some very useful information on Session State and it's locking behaviors.

现在,要解决您的问题,您将不得不做几件事,这取决于您使用的是MVC控制器还是编写自定义的IHttpHandler.

Now, To take care of your problem, you're going to have to do a couple things which depend on whether you're using MVC controllers or if you're writing a custom IHttpHandler.

  • 如果您正在编写自己的IHttpHandler,请确保已将IRequiresSessionStateIReadOnlySessionState接口添加到处理程序中.这样,管道将跳过寻找会话并直接进行处理. context.Session在这种情况下将为空.
  • 如果您使用MVC处理请求,则需要使用
  • If you're writing your own IHttpHandler, make sure you do not have the IRequiresSessionState or IReadOnlySessionState interfaces added to your handler. In doing so, the pipeline will skip looking for a session and go straight to processing. context.Session will be null in this situation.
  • If you're using MVC to process the request, you'll need to decorate your controller class with the SessionState attribute passing in the SessionStateBehavior of SessionStateBehavior.Disabled.

无论哪种情况,您都将不能依靠Session对象来存储您的上传状态.您可以根据他们的SessionID创建一个静态的ConcurrentDictionary键(您将需要传递该值,或者您需要传入上传查询字符串或自己读取Cookie,调用Session.SessionId只会再次阻止您),然后在其中存储您的上传状态(看起来它们也是并发*.

In either case you won't be able to rely on the Session object to store your upload statuses. You can create a static ConcurrentDictionary keyed off of their SessionID (which you'll either need to pass in the upload query string or read the cookie yourself, calling Session.SessionId will just block you again) and store your upload statuses in there (which look like they're Concurrent* as well).

另一种选择是将SessionStateProvider替换为您自己的自定义提供程序,但是在这种情况下可能会显得过大.

Another option would be to replace the SessionStateProvider with your own custom provider but that might be overkill in this situation.

这篇关于ASP.Net异步HTTP文件上传处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆