Uploading files using flow.js + ng-flow in WebAPI 2

I am trying to use flow.js ( https://github.com/flowjs/flow.js ) through my Angular packaging ( https://github.com/flowjs/ng-flow/tree/master/samples/basic ) to download files to the ASP.NET WebAPI 2 server. Anyway, when I select the file to upload my WebAPI, it just receives the first request of the GET package and then nothing happens: POST is not executed and it seems that flow.js did not start loading.

The initial GET that starts when a file is selected is as follows:

GET http://localhost:49330/api/upload?flowChunkNumber=1&flowChunkSize=1048576&flowCurrentChunkSize=4751&flowTotalSize=4751&flowIdentifier=4751-ElmahMySqlsql&flowFilename=Elmah.MySql.sql&flowRelativePath=Elmah.MySql.sql&flowTotalChunks=1 HTTP/1.1 Host: localhost:49330 Connection: keep-alive User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36 Accept: */* Referer: http://localhost:49330/ Accept-Encoding: gzip, deflate, sdch Accept-Language: en-US,en;q=0.8,it;q=0.6 

And the answer is:

 HTTP/1.1 202 Accepted Cache-Control: no-cache Pragma: no-cache Expires: -1 Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcNDViXFRlc3RcVXBUZXN0XFVwVGVzdFxhcGlcdXBsb2Fk?= X-Powered-By: ASP.NET Date: Fri, 17 Apr 2015 08:02:56 GMT Content-Length: 0 

Then, requests are no longer issued.

There seems to be no modern WebAPI example, but only the scattered messages I created for beginners like me, a dummy playback solution that you can download from http://1drv.ms/1CSF5jq : this is an ASP.NET WebAPI solution 2, where I posted the download code in the home view after adding the appropriate API controller. Just hit F5 and try downloading the file. You can find the API controller in UploadController.cs .

Relevant parts of the code:

a) client side : a page similar to the example of quick launch of the ng-flow page:

 <div class="row"> <div class="col-md-12"> <div flow-init="{target: '/api/upload'}" flow-files-submitted="$flow.upload()" flow-file-success="$file.msg = $message"> <input type="file" flow-btn /> <ol> <li ng-repeat="file in $flow.files">{{file.name}}: {{file.msg}}</li> </ol> </div> </div> </div> 

The corresponding code is essentially an empty TS skeleton with module initialization:

 module Up { export interface IMainScope { } export class MainController { public static $inject = ["$scope"]; constructor(private $scope: IMainScope) { } } var app = angular.module("app", ["flow"]); app.controller("mainController", MainController); } 

b) server side . I added several packages for the necessary scripts and the following controller, modified from the sample code that I found in How to upload a file in chunks in ASP.NET using an ng stream . Please note that in the GET Upload method I changed the signature using the binding model (otherwise we would have got 404, because the route was not mapped), and when the piece was not found, I return the code 202 - Accepted , not 404, since the documentation flow.js says that 200 corresponds to “the piece was accepted and fixed. There is no need to re-download”, while 404 cancels the entire download, and any other code (for example, 202 here) tells the loader to try again.

 [RoutePrefix("api")] public class UploadController : ApiController { private readonly string _sRoot; public UploadController() { _sRoot = HostingEnvironment.MapPath("~/App_Data/Uploads"); } [Route("upload"), AcceptVerbs("GET")] public IHttpActionResult Upload([FromUri] UploadBindingModel model) { if (IsChunkHere(model.FlowChunkNumber, model.FlowIdentifier)) return Ok(); return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Accepted)); } [Route("upload"), AcceptVerbs("POST")] public async Task<IHttpActionResult> Upload() { // ensure that the request contains multipart/form-data if (!Request.Content.IsMimeMultipartContent()) throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); if (!Directory.Exists(_sRoot)) Directory.CreateDirectory(_sRoot); MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(_sRoot); try { await Request.Content.ReadAsMultipartAsync(provider); int nChunkNumber = Convert.ToInt32(provider.FormData["flowChunkNumber"]); int nTotalChunks = Convert.ToInt32(provider.FormData["flowTotalChunks"]); string sIdentifier = provider.FormData["flowIdentifier"]; string sFileName = provider.FormData["flowFilename"]; // rename the generated file MultipartFileData chunk = provider.FileData[0]; // Only one file in multipart message RenameChunk(chunk, nChunkNumber, sIdentifier); // assemble chunks into single file if they're all here TryAssembleFile(sIdentifier, nTotalChunks, sFileName); return Ok(); } catch (Exception ex) { return InternalServerError(ex); } } private string GetChunkFileName(int chunkNumber, string identifier) { return Path.Combine(_sRoot, String.Format(CultureInfo.InvariantCulture, "{0}_{1}", identifier, chunkNumber)); } private void RenameChunk(MultipartFileData chunk, int chunkNumber, string identifier) { string sGeneratedFileName = chunk.LocalFileName; string sChunkFileName = GetChunkFileName(chunkNumber, identifier); if (File.Exists(sChunkFileName)) File.Delete(sChunkFileName); File.Move(sGeneratedFileName, sChunkFileName); } private string GetFileName(string identifier) { return Path.Combine(_sRoot, identifier); } private bool IsChunkHere(int chunkNumber, string identifier) { string sFileName = GetChunkFileName(chunkNumber, identifier); return File.Exists(sFileName); } private bool AreAllChunksHere(string identifier, int totalChunks) { for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++) if (!IsChunkHere(nChunkNumber, identifier)) return false; return true; } private void TryAssembleFile(string identifier, int totalChunks, string filename) { if (!AreAllChunksHere(identifier, totalChunks)) return; // create a single file string sConsolidatedFileName = GetFileName(identifier); using (Stream destStream = File.Create(sConsolidatedFileName, 15000)) { for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++) { string sChunkFileName = GetChunkFileName(nChunkNumber, identifier); using (Stream sourceStream = File.OpenRead(sChunkFileName)) { sourceStream.CopyTo(destStream); } } //efor destStream.Close(); } // rename consolidated with original name of upload // strip to filename if directory is specified (avoid cross-directory attack) filename = Path.GetFileName(filename); Debug.Assert(filename != null); string sRealFileName = Path.Combine(_sRoot, filename); if (File.Exists(filename)) File.Delete(sRealFileName); File.Move(sConsolidatedFileName, sRealFileName); // delete chunk files for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++) { string sChunkFileName = GetChunkFileName(nChunkNumber, identifier); File.Delete(sChunkFileName); } //efor } } 
+8
javascript angularjs typescript asp.net-web-api2 flow-js
source share
1 answer

200 status is not the only one that is considered successful. 201 and 202 too. Check out the successStatuses option: https://github.com/flowjs/flow.js/blob/master/dist/flow.js#L91 So you only need to change the status to 204, which means No Content .

+5
source share

All Articles