2015-04-17 10 views
8

Sto provando a utilizzare flow.js (https://github.com/flowjs/flow.js) tramite il relativo wrapper angolare (https://github.com/flowjs/ng-flow/tree/master/samples/basic) per caricare file su un server WebAPI 2 di ASP.NET. Ad ogni modo, quando seleziono un file per caricare il mio WebAPI, ottengo la prima richiesta GET e poi non succede nulla: non viene eseguito alcun POST e sembra che flow.js non abbia avviato il caricamento.Caricamento di file con flow.js + ng-flow in WebAPI 2

Il GET iniziale licenziati quando si seleziona un file è:

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 

e la risposta è:

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 

Poi, senza ulteriori richieste vengono emesse.

Come sembra che non ci sia un esempio WebAPI aggiornato, ma solo post sparsi, ho creato per principianti come me una soluzione di repro fittizia che è possibile scaricare da http://1drv.ms/1CSF5jq: è una soluzione Web 2.0 di ASP.NET in cui ho inserito il codice di caricamento nella vista home, dopo aver aggiunto il controller API corrispondente. Basta premere F5 e provare a caricare un file. È possibile trovare il controller API in UploadController.cs.

Le parti di codice rilevanti sono:

a) client lato: una pagina simile all'esempio di avvio rapido della pagina ng-flow:

<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> 

il codice corrispondente è essenzialmente un scheletro vuoto TS con l'inizializzazione modulo:

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) lato server: Ho aggiunto alcuni pacchetti per gli script richiesti e il seguente controller, modificato dal codice di esempio che ho trovato su How to upload file in chunks in ASP.NET using ng-Flow. Si noti che nel metodo GET Upload ho cambiato la firma utilizzando un modello di associazione (altrimenti avremmo ottenuto un 404 poiché la route non corrispondeva) e quando il chunk non viene trovato restituisco un codice 202 - Accepted anziché 404, come flow.js la documentazione dice che 200 corrisponde a "Il chunk è stato accettato e corretto, non c'è bisogno di ricaricare", mentre un 404 annulla l'intero caricamento, e qualsiasi altro codice (come 202 qui) dice all'autore del caricamento di riprovare.

[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 
    } 
} 
+0

Devo aggiungere che in base a https://github.com/flowjs/ng-flow/issues/144 e apparentemente in contrasto con la documentazione, sembra che 404 debba essere restituito da GET quando il chunk non viene trovato. Ho provato questo, ma nulla cambia e nessun caricamento inizia. – Naftis

risposta

5

200 stato non è l'unico considerato un successo. 201 e 202 lo sono anche. Leggi l'opzione di successStatuses: https://github.com/flowjs/flow.js/blob/master/dist/flow.js#L91 Quindi, il solo cambiamento necessario è quello di restituire uno stato 204, che significa No Content.

+2

Grazie, ora iniziano i miei caricamenti! Pertanto, per chiunque sia interessato, è sufficiente modificare il valore restituito nel metodo GET nel controller API per restituire ResponseMessage (new HttpResponseMessage (HttpStatusCode.NoContent)); – Naftis

Problemi correlati