2015-04-17 52 views
8

我試圖通過它的Angular包裝器(https://github.com/flowjs/ng-flow/tree/master/samples/basic)使用flow.js(https://github.com/flowjs/flow.js)將文件上傳到ASP.NET WebAPI 2服務器。無論如何,當我選擇一個文件上傳我的WebAPI只是得到第一個塊GET請求,然後什麼都沒有發生:沒有POST完成,似乎flow.js沒有開始上傳。使用flow.js + ng-flow將文件上傳到WebAPI 2

最初被解僱時,我選擇一個文件是:

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 

和響應是:

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 

然後,沒有更多的請求發出。

因爲它似乎沒有最新的WebAPI示例,但只有分散的帖子,我爲像我這樣的新手創建了一個虛擬的repro解決方案,您可以從http://1drv.ms/1CSF5jq下載:這是一個ASP.NET WebAPI 2解決方案,在添加相應的API控制器後,在主視圖中上傳代碼。只需點擊F5並嘗試上傳文件。你可以在UploadController.cs找到API控制器。

相關的代碼部分是:

一個)客戶端:類似於納克流頁的快速啓動一例的頁面:

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

相應的代碼基本上是一個空TS骨架與模塊初始化:

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)中服務器側:我爲所需的腳本添加了一些補丁,並添加了以下控制器,這些控制器是從How to upload file in chunks in ASP.NET using ng-Flow中找到的示例代碼修改而來的。請注意,在GET Upload方法中,我使用綁定模型更改了簽名(否則我們將得到404,因爲路由未匹配),並且當未找到塊時,我會返回202 - Accepted代碼而不是404,因爲flow.js文檔說200對應於「塊被接受並且正確,不需要重新上傳」,而404取消了整個上傳,而其他任何代碼(如202這裏)都會告訴上傳者重試。

[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

我必須添加,根據https://github.com/flowjs/ng-flow/issues/144和貌似與文檔形成對比,似乎404應該從GET返回時,塊沒有找到。我嘗試過,但沒有任何變化,沒有開始上傳。 – Naftis

回答

5

200地位不是唯一被認爲是成功的地位。 201和202也是。 閱讀關於successStatuses的選項: https://github.com/flowjs/flow.js/blob/master/dist/flow.js#L91 所以只需要改變就是返回204狀態,這意味着No Content

+2

謝謝,現在我的上傳開始了!因此,對於任何可能感興趣的用戶,只需更改API控制器中GET方法的返回值以返回ResponseMessage(new HttpResponseMessage(HttpStatusCode.NoContent)); – Naftis

相關問題