2015-04-17 20 views
8

Tôi đang cố gắng sử dụng flow.js (https://github.com/flowjs/flow.js) qua trình bao bọc Góc (https://github.com/flowjs/ng-flow/tree/master/samples/basic) để tải tệp lên máy chủ ASP.NET WebAPI 2. Dù sao khi tôi chọn một tệp để tải lên WebAPI của tôi chỉ nhận được yêu cầu GET đoạn đầu tiên và sau đó không có gì xảy ra: không có POST nào được thực hiện và dường như flow.js không bắt đầu tải lên.Tải tệp lên với flow.js + ng-flow tới WebAPI 2

Các GET ban đầu phát sinh khi tôi chọn một tập tin là:

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 

Và câu trả lời là:

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 

Sau đó, không còn phải yêu cầu được phát hành. Có vẻ như không có ví dụ WebAPI cập nhật, nhưng chỉ có các bài viết rải rác, tôi đã tạo cho những người mới như tôi một giải pháp repro giả mà bạn có thể tải xuống từ http://1drv.ms/1CSF5jq: đó là giải pháp ASP.NET WebAPI 2 mà tôi đã đặt mã tải lên ở chế độ xem nhà, sau khi thêm bộ điều khiển API tương ứng. Chỉ cần nhấn F5 và thử tải lên một tệp. Bạn có thể tìm thấy bộ điều khiển API trong UploadController.cs.

Các bộ phận mã có liên quan bao gồm:

a) client bên: một trang giống như ví dụ nhanh chóng khởi động của trang ng-dòng chảy:

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

Mã tương ứng là chủ yếu một skeleton TS trống với việc khởi tạo mô-đun:

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) phía máy chủ: Tôi đã thêm một số gói cho các tập lệnh được yêu cầu và bộ điều khiển sau, được sửa đổi từ mã mẫu tôi tìm thấy tại How to upload file in chunks in ASP.NET using ng-Flow. Lưu ý rằng trong phương thức GET Upload, tôi đã thay đổi chữ ký bằng cách sử dụng mô hình ràng buộc (nếu không chúng ta sẽ nhận được 404 khi tuyến đường không khớp) và khi không tìm thấy đoạn đó, tôi trả lại mã 202 - Accepted thay vì 404, như flow.js tài liệu nói rằng 200 tương ứng với "Đoạn văn đã được chấp nhận và chính xác. Không cần phải tải lên lại", trong khi 404 hủy tải lên toàn bộ và bất kỳ mã nào khác (như 202 ở đây) yêu cầu người tải lên thử lại.

[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

Tôi phải thêm điều đó theo https://github.com/flowjs/ng-flow/issues/144 và dường như trái ngược với tài liệu, có vẻ như 404 phải được trả lại từ GET khi không tìm thấy đoạn đó. Tôi đã thử điều này, nhưng không có gì thay đổi và không bắt đầu tải lên. – Naftis

Trả lời

5

Trạng thái 200 không chỉ là trạng thái duy nhất được coi là thành công. 201 và 202 cũng vậy. Đọc về tùy chọn của successStatuses: https://github.com/flowjs/flow.js/blob/master/dist/flow.js#L91 Vì vậy, chỉ thay đổi bạn cần là trả lại trạng thái 204, nghĩa là No Content.

+2

Cảm ơn, giờ video của tôi đã bắt đầu! Vì vậy, đối với bất kỳ ai có thể quan tâm, chỉ cần thay đổi giá trị trả về trong phương thức GET trong bộ điều khiển API để trả về ResponseMessage (HttpResponseMessage mới (HttpStatusCode.NoContent)); – Naftis

Các vấn đề liên quan