Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Async File Uploads with chunking to ASP.Net Web API

I have read a number of closely related questions but not one that hits this exactly. If it is a duplicate, please send me a link.

I am using an angular version of the flowjs library for doing HTML5 file uploads (https://github.com/flowjs/ng-flow). This works very well and I am able to upload multiple files simultaneously in 1MB chunks. There is an ASP.Net Web API Files controller that accepts these and saves them on disk. Although I can make this work, I am not doing it efficiently and would like to know a better approach.

First, I used the MultipartFormDataStreamProvider in an async method that worked great as long as the file uploaded in a single chunk. Then I switched to just using the FileStream to write the file to disk. This also worked as long as the chunks arrived in order, but of course, I cannot rely on that.

Next, just to see it work, I wrote the chunks to individual file streams and combined them after the fact, hence the inefficiency. A 1GB file would generate a thousand chunks that needed to be read and rewritten after the upload was complete. I could hold all file chunks in memory and flush them after they are all uploaded, but I'm afraid the server would blow up.

It seems that there should be a nice asynchronous solution to this dilemma but I don't know what it is. One possibility might be to use async/await to combine previous chunks while writing the current chunk. Another might be to use Begin/EndInvoke to create a separate thread so that the file manipulation on disk was handled independent of the thread reading from the HttpContext but this would rely on the ThreadPool and I'm afraid that the created threads will be unduly terminated when my MVC controller returns. I could create a FileWatcher that ran completely independent of ASP.Net but that would be very kludgey.

So my questions are, 1) is there a simple solution already that I am missing? (seems like there should be) and 2) if not, what is the best approach to solving this inside the Web API framework?

Thanks, bob

like image 508
bob Avatar asked May 07 '14 14:05

bob


2 Answers

I'm not familiar with that kind of chunked upload, but I believe this should work:

  • Use flowTotalSize to pre-allocate the file when the first chunk comes in.
  • Have one SemaphoreSlim per file to serialize the asynchronous writes for that file.
  • Each chunk will write to its own offset (flowChunkSize * (flowChunkNumber - 1)) within the file.

This doesn't handle situations where the uploads are unexpectedly terminated. That kind of solution usually involves allocating/writing a temporary file (with a special extension) and then moving/renaming that file once the last chunk arrives.

Don't forget to ensure that your file writing is actually asynchronous.

like image 126
Stephen Cleary Avatar answered Nov 10 '22 09:11

Stephen Cleary


Using @Stephen Cleary's answer, and this thread: https://github.com/flowjs/ng-flow/issues/41 I was able to make an ASP.NET Web Api Implementation and uploaded it for those still wondering about this question such as @Herb Caudill

https://github.com/samhowes/NgFlowSample/tree/master.

The original answer is the real answer to this question, but I don't have enough reputation yet to comment. I did not use a SemaphoreSlim, but instead enabled file Write sharing. But did in fact pre-allocate and make sure that each chunk is getting written to the right location by calculating an offset.

I will be contributing this to the Flow samples at: https://github.com/flowjs/flow.js/tree/master/samples

like image 7
Sam Avatar answered Nov 10 '22 08:11

Sam