ASP.Net asynchronous HTTP file upload handler

I am trying to make a file upload handler in C # that is asynchronous and can provide updates on the progress of a file through asynchronous AJAX requests. Basically, if the request is POST, it loads some information into the session, and then starts loading, if the request was GET, it returns the current state of the load (bytes downloaded, general bytes, etc.). I'm not quite sure if this should be an asynchronous handler, but the files can be quite large, so I thought this would work best. For the base async handler, I used something very similar to the handler in this MSDN article . I have posted below some key sections of my code below. The problem I am facing is that I do not receive any GET information until the completion of the POST. I mentioned that in this example, I use jQuery for GET requests and BlueImp to publish the file.

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

The request method of an asynchronous process simply calls the correct method, whether it is POST or GET, one of the following, and then calls CompleteRequest to complete 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; } } 

I tried a lot of things, such as non-asynchronous handlers, async handlers, making sure JavaScript is running async, but for now I think I need different eyes on the problem, so thanks for any help anyone can provide.

+4
source share
1 answer

I assume that you are using the default ASP.Net session manager, and I see that you are calling GetSessionStore to get the session. Unfortunately, the default session manager serializes all requests when a call requires write access to the session store. This is fooobar.com/questions/21246 / ... and this MSDN Arbitrator in session state contains very useful session state information and behavior blocking.

Now, to take care of your problem, you will need to do a couple of things that depend on whether you use MVC controllers or write custom IHttpHandler.

  • If you are writing your own IHttpHandler, make sure that you do not have the IRequiresSessionState or IReadOnlySessionState interfaces added to your handler. In this case, the pipeline will skip the search for the session and immediately proceed to processing. context.Session in this situation will be null.
  • If you use MVC to process the request, you need to decorate your controller class with the SessionState attribute , passing in SessionStateBehavior SessionStateBehavior.Disabled .

In any case, you cannot rely on the Session object to store the download status. You can create a static ConcurrentDictionary that disconnects from their SessionID (which you will either need to pass to the download request line or read the cookie yourself by calling Session.SessionId, it will simply block you) and save your download statuses there (which looks like they also Concurrent *).

Another option is to replace SessionStateProvider with your own custom provider, but in this situation this may be redundant.

+4
source

Source: https://habr.com/ru/post/1442323/


All Articles