ASP.Net异步HTTP文件上传处理程序

时间:2012-10-26 14:22:44

标签: c# javascript asp.net asynchronous httphandler

我试图在C#中创建一个异步的文件上传处理程序,并可以通过AJAX异步请求提供文件进度的更新。基本上,如果请求是POST,它会将一些信息加载到会话中,然后开始上传,如果请求是GET,则返回上载的当前状态(上载的字节数,总字节数等)。我不完全确定它需要是一个异步处理程序,但文件可能非常大,所以我认为这将是最好的。对于基本异步处理程序,我使用了与此MSDN article中的处理程序非常相似的东西。我已经在下面的代码的一些关键部分下面发布了。我遇到的问题是,在POST完成之前,我没有收到任何GET信息。我将在本例中提及我使用jQuery进行GET请求,使用BlueImp发布文件。

HTML和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来结束请求:

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运行异步,但此时我觉得我需要对这个问题有一些不同的看法,所以谢谢你的帮助。可以提供。

1 个答案:

答案 0 :(得分:4)

我假设您正在使用默认的ASP.Net会话管理器,我看到您致电GetSessionStore来获取会话。不幸的是,默认会话管理器在呼叫需要对会话存储的写访问权时序列化所有请求。这个StackOverflow question和这个MSDN arcle on Session State有一些关于会话状态和锁定行为的非常有用的信息。

现在,为了解决您的问题,您将不得不做一些取决于您是使用MVC控制器还是编写自定义IHttpHandler的事情。

  • 如果您正在编写自己的IHttpHandler,请确保IRequiresSessionStateIReadOnlySessionState接口添加到您的处理程序中。在这样做时,管道将跳过寻找会话并直接进行处理。在这种情况下,context.Session将为空。
  • 如果您正在使用MVC处理请求,则需要使用SessionState attribute传递SessionStateBehavior.Disabled {{1}}来装饰您的控制器类。

在任何一种情况下,您都无法依赖Session对象来存储您的上传状态。你可以创建一个静态的ConcurrentDictionary来锁定它们的SessionID(你需要传递上传查询字符串或者自己读取cookie,调用Session.SessionId会再次阻止你)并将你的上传状态存储在那里(看起来他们也是并发*。

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