Basilf
Basilf

Reputation: 461

WebAPI return response conditionally and async

I have some sort of an upload feature, which takes an image and resizes the image to different sizes, then returns a path of the 100x100 image to the user. In my case, the user is going to wait until all resizing and uploading is finished, which is obviously not good for the user experience. I Tried making my ActionResult as a Async Task, but im having trouble doing it. This is my code right now, which waits until all uploads are completed.

 [HttpPost]
    [Route("UploadProfileImage")]
    public async Task<IHttpActionResult> UploadProfileImage()
    {
        string userId= Identifier.GetIdentifier();
        ReturnResult response = new ReturnResult();
        try
        {

            var provider = new MultipartMemoryStreamProvider();
            await Request.Content.ReadAsMultipartAsync(provider);
            var fileNameParam = provider.Contents[0].Headers.ContentDisposition.Parameters.FirstOrDefault(p => p.Name.ToLower() == "filename");
            string fileName = (fileNameParam == null) ? "" : fileNameParam.Value.Trim('"');
            byte[] file = await provider.Contents[0].ReadAsByteArrayAsync();
            MemoryStream imgStream = new MemoryStream(file);
            Bitmap mg = new Bitmap(imgStream);
            List<Size> sizes = new List<Size>();
            sizes.Add(new Size { Width = mg.Width, Height = mg.Height });
            sizes.Add(new Size { Width = 100, Height = 100 });
            sizes.Add(new Size { Width = 300, Height = 300 });
            sizes.Add(new Size { Width = 500, Height = 500 });
            sizes.Add(new Size { Width = 800, Height = 800 });
            int index = 0;
            foreach (var size in sizes)
            {

                byte[] newImageByteArray = Helpers.CreateImageThumbnail(file, Convert.ToInt32(size.Width), Convert.ToInt32(size.Height));
                Helpers.SaveProfileImage(newImageByteArray, agentId, size, index == 0 ? true : false);
                index++;
               //---*--->>Here I Should check here that if original & 100x100 image were uploaded then return a response and continue uploading the others (if(index>1))
            }
            var path = "/users/uploads/100/" + userId+ ".jpg" + "?v=" + Guid.NewGuid();


            UserManager.UpdateUploadImage(userId, path);
            response.ReturnObject = path;
            response.ReturnCode = ReturnCodes.Success;
            return Ok(response);
        }
        catch (Exception ex)
        {
            response.ReturnObject = ex;
            response.ReturnCode = ReturnCodes.Exception;
            return Ok(response);
        }

Note: the client is angularJS app that posts to the webapi server. Any idea how to rewrite the code in order to achieve this?

Upvotes: 0

Views: 297

Answers (1)

Stephen Cleary
Stephen Cleary

Reputation: 456497

I Tried making my ActionResult as a Async Task, but im having trouble doing it.

That's not the correct solution, since async doesn't change the HTTP protocol (as I describe on my blog). It can't be the correct solution because of this:

then returns a path of the 100x100 image to the user.

How would a web request return a response to the user, and then later return again (to return the path)? This isn't possible with a request/response protocol like HTTP; you only get one response per request.

In my case, the user is going to wait until all resizing and uploading is finished, which is obviously not good for the user experience.

Your options are:

  1. Put in a complex solution that tracks work independent of an HTTP request (this usually requires a reliable queue like an Azure Queue connected to an independent worker service like an Azure Function), and then provide some means to proactively notify the UI of completion (SignalR is good for this, unless you need to scale really big in which case polling could actually be better).
  2. Use AJAX on the client side.

Note that you're already doing (2):

the client is angularJS app that posts to the webapi server.

So, all you have to do is change the client so that it doesn't wait for the request before allowing the user to do other things. No server changes necessary.

Upvotes: 3

Related Questions