Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net Web API: Send response before request body is read/uploaded

I have an action on my controller to which large (500mb - 2gb) files are supposed to be uploaded. Eg,

[HttpPost]
public void PostFile([FromUri]Guid uploadId)
{
  ...
}

Inside the body, a couple of checks are performed (such as if the uploadId exists), and then the HTTP Request's posted body is written to disk (/network share).

I thought I had this nailed, in that I could reject the HTTP request almost immediately if the uploadId hadn't been created, by throwing a HttpResponseException, but I've just discovered that even in this case, the Request's body gets fully uploaded to the server and saved to disk the ASP.Net Temporary Files folder. This means that the client has to fully upload a file (taking approximately 5-10 minutes) just to find out that it has been rejected by the server.

How do I avoid this? Is there any way to prevent IIS from writing the body to disk, and allowing the controller to handle the stream? I find it frustrating that there is a 5 minute delay between the client starting the upload, and the request hitting my own code, because ASP.Net has to do its own thing and write the upload to local disk.


EDIT

I've been asked how I'm reading the upload data. Essentially, using this method:

        /// <summary>
        /// Saves the current request's upload stream to the proivded path.
        /// </summary>
        /// <param name="destinationPath">The path to save to.</param>
        /// <returns></returns>
        private async Task<FileInfo> SaveUploadToDiskAsync(string destinationPath)
        {
            using (var httpContent = Request.Content)
            {
                using (FileStream fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
                {
                    await httpContent.CopyToAsync(fileStream);

                    return new FileInfo(destinationPath);
                }
            }
        }
like image 831
dark_perfect Avatar asked Dec 28 '25 01:12

dark_perfect


2 Answers

You can configure Web API to work in bufferless mode (streaming) for a specific controller (or globally, for that matter) by implementing a custom WebHostBufferPolicySelector

public class NoBufferPolicySelector : WebHostBufferPolicySelector
{
   public override bool UseBufferedInputStream(object hostContext)
   {
      var context = hostContext as HttpContextBase;

      if (context != null)
      {
         if (string.Equals(context.Request.RequestContext.RouteData.Values["controller"].ToString(), "upload", StringComparison.InvariantCultureIgnoreCase))
            return false;
      }

      return true;
   }

   public override bool UseBufferedOutputStream(HttpResponseMessage response)
   {
      return base.UseBufferedOutputStream(response);
   }
}

I have written a walk through for dealing large files in Web API

like image 104
Filip W Avatar answered Dec 30 '25 15:12

Filip W


The easiest solution would probably be to separate out the calls where there is one for CreateUploadID and another for UploadIDData. This assumes that you have control over the client, where you can definitively say that the upload with data only happens after an ID check.

If you want to do it all in one request (check ID before parsing request) you may have to hook the application event AuthenticateRequest in your Application class or in an HttpModule (I recommend the module). You can visit this page for a description of the events available to a module, and under AuthenticateRequest it says

Call this event when a security module needs to authenticate the user before it processes the request.

Unfortunately, it might not work because "processes the request" might mean .NET processing of the request after it has been written to disk by IIS or .NET writes POST'ed contents of the request to disk. It depends on when the writing happens, like you ask, and IHttpModule will provide you with as low a level of abstraction as .NET will allow. If it doesn't work, you'll have to use something native or another platform to accomplish what you want.

like image 28
welegan Avatar answered Dec 30 '25 16:12

welegan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!