2015-04-17 18 views
8

Próbuję użyć flow.js (https://github.com/flowjs/flow.js) za pomocą opakowania Angular (https://github.com/flowjs/ng-flow/tree/master/samples/basic) w celu przesłania plików na serwer ASP.NET WebAPI 2. W każdym razie, kiedy wybieram plik do przesłania, mój WebAPI właśnie pobiera pierwsze żądanie GET, a potem nic się nie dzieje: nie jest wykonywany POST i wygląda na to, że plik flow.js nie rozpoczął wysyłania.Przesyłanie plików za pomocą flow.js + ng-flow do WebAPI 2

Początkowy się zwolniony, kiedy wybrać plik jest:

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 

A odpowiedź brzmi:

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 

Wtedy nie więcej żądań są wydawane.

Wygląda na to, że nie ma aktualnego przykładu WebAPI, ale tylko rozproszone posty, które stworzyłem dla początkujących, takich jak ja, atypowego rozwiązania repro, które można pobrać z http://1drv.ms/1CSF5jq: jest to rozwiązanie ASP.NET WebAPI 2, w którym umieściłem kod do przesłania w widoku głównym, po dodaniu odpowiedniego kontrolera API. Po prostu naciśnij F5 i spróbuj załadować plik. Możesz znaleźć kontroler API w UploadController.cs.

Odpowiednie elementy kodu są:

a) stronie klienta: strona podobny do szybkiego rozpoczęcia przykładzie strony ng przepływu:

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

odpowiedni kod jest zasadniczo puste TS szkielet z inicjalizacji module

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) strony serwera: Dodałem kilka wiązań dla wymaganych skryptów i następującego kontrolera, zmodyfikowanego na podstawie przykładowego kodu znalezionego pod adresem How to upload file in chunks in ASP.NET using ng-Flow. Zauważ, że w metodzie GET Upload zmieniłem sygnaturę przy użyciu modelu wiążącego (w przeciwnym razie uzyskalibyśmy 404, ponieważ trasa nie została dopasowana), a gdy fragment nie zostanie znaleziony, zwracam kod 202 - Accepted zamiast 404, jako flow.js dokumentacja mówi, że 200 odpowiada "Fragment został zaakceptowany i poprawny, nie trzeba ponownie przesyłać", podczas gdy 404 anuluje cały ładowanie, a każdy inny kod (na przykład 202 tutaj) informuje przesyłającego, aby spróbował ponownie.

[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

Muszę dodać, że zgodnie z https://github.com/flowjs/ng-flow/issues/144 i pozornie w przeciwieństwie do dokumentacji, wydaje się, że 404 powinno zostać zwrócone z GET, gdy porcja nie zostanie znaleziona. Próbowałem tego, ale nic się nie zmienia i nie rozpoczyna się wysyłanie. – Naftis

Odpowiedz

5

Status 200 nie jest jedynym uznanym za sukces. 201 i 202 też. Przeczytaj o opcji: successStatuses: https://github.com/flowjs/flow.js/blob/master/dist/flow.js#L91 Tak więc wystarczy zmienić status na 204, co oznacza No Content.

+2

Dzięki, teraz moje zdjęcia zaczynają się! Tak więc dla każdego, kto może być zainteresowany, po prostu zmień wartość zwracaną w metodzie GET w kontrolerze API, aby zwrócić ResponseMessage (nowe HttpResponseMessage (HttpStatusCode.NoContent)); – Naftis

Powiązane problemy