2015-09-21 9 views
9

にアップロードするときに私が達成したいタスクがAzureストレージにファイルをアップロードするために、ウェブAPIサービスを作成することです進捗状況を取得します。同時に、私は実際のアップロードの進行状況を反映する進捗インジケータを持っていたいと思います。いくつかの研究と勉強した後、私は、2つの重要な事柄を発見:ウェブAPI - Azureストレージ

まず、私はチャンクに手動でファイルを分割し、非同期Microsoft.WindowsAzure.Storage.dllからPutBlockAsync方法を使用してそれらをアップロードする必要があります。第二に

は、私はストリーム・モードでの私のWeb APIサービスではなくバッファ・モードでファイルを受信しなければならないということです。私もthisブログの記事としてストリーム・モードを有効に

UploadController.cs

using System.Configuration; 
using System.Net; 
using System.Net.Http; 
using System.Threading.Tasks; 
using System.Web.Http; 
using Microsoft.WindowsAzure.Storage; 
using Microsoft.WindowsAzure.Storage.Blob; 
using WebApiFileUploadToAzureStorage.Infrastructure; 
using WebApiFileUploadToAzureStorage.Models; 

namespace WebApiFileUploadToAzureStorage.Controllers 
{ 
    public class UploadController : ApiController 
    { 
     [HttpPost] 
     public async Task<HttpResponseMessage> UploadFile() 
     { 
      if (!Request.Content.IsMimeMultipartContent("form-data")) 
      { 
       return Request.CreateResponse(HttpStatusCode.UnsupportedMediaType, 
        new UploadStatus(null, false, "No form data found on request.", string.Empty, string.Empty)); 
      } 

      var streamProvider = new MultipartAzureBlobStorageProvider(GetAzureStorageContainer()); 
      var result = await Request.Content.ReadAsMultipartAsync(streamProvider); 

      if (result.FileData.Count < 1) 
      { 
       return Request.CreateResponse(HttpStatusCode.BadRequest, 
        new UploadStatus(null, false, "No files were uploaded.", string.Empty, string.Empty)); 
      } 

      return Request.CreateResponse(HttpStatusCode.OK); 
     } 

     private static CloudBlobContainer GetAzureStorageContainer() 
     { 
      var storageConnectionString = ConfigurationManager.AppSettings["AzureBlobStorageConnectionString"]; 
      var storageAccount = CloudStorageAccount.Parse(storageConnectionString); 

      var blobClient = storageAccount.CreateCloudBlobClient(); 
      blobClient.DefaultRequestOptions.SingleBlobUploadThresholdInBytes = 1024 * 1024; 

      var container = blobClient.GetContainerReference("photos"); 

      if (container.Exists()) 
      { 
       return container; 
      } 

      container.Create(); 

      container.SetPermissions(new BlobContainerPermissions 
      { 
       PublicAccess = BlobContainerPublicAccessType.Container 
      }); 

      return container; 
     } 
    } 
} 

MultipartAzureBlobStorageProvider.cs

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.Linq; 
using System.Net.Http; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 
using Microsoft.WindowsAzure.Storage.Blob; 

namespace WebApiFileUploadToAzureStorage.Infrastructure 
{ 
    public class MultipartAzureBlobStorageProvider : MultipartFormDataStreamProvider 
    { 
     private readonly CloudBlobContainer _blobContainer; 

     public MultipartAzureBlobStorageProvider(CloudBlobContainer blobContainer) : base(Path.GetTempPath()) 
     { 
      _blobContainer = blobContainer; 
     } 

     public override Task ExecutePostProcessingAsync() 
     { 
      const int blockSize = 256 * 1024; 
      var fileData = FileData.First(); 
      var fileName = Path.GetFileName(fileData.Headers.ContentDisposition.FileName.Trim('"')); 
      var blob = _blobContainer.GetBlockBlobReference(fileName); 
      var bytesToUpload = (new FileInfo(fileData.LocalFileName)).Length; 
      var fileSize = bytesToUpload; 

      blob.Properties.ContentType = fileData.Headers.ContentType.MediaType; 
      blob.StreamWriteSizeInBytes = blockSize; 

      if (bytesToUpload < blockSize) 
      { 
       var cancellationToken = new CancellationToken(); 

       using (var fileStream = new FileStream(fileData.LocalFileName, FileMode.Open, FileAccess.ReadWrite)) 
       { 
        var upload = blob.UploadFromStreamAsync(fileStream, cancellationToken); 

        Debug.WriteLine($"Status {upload.Status}."); 

        upload.ContinueWith(task => 
        { 
         Debug.WriteLine($"Status {task.Status}."); 
         Debug.WriteLine("Upload is over successfully."); 
        }, TaskContinuationOptions.OnlyOnRanToCompletion); 

        upload.ContinueWith(task => 
        { 
         Debug.WriteLine($"Status {task.Status}."); 

         if (task.Exception != null) 
         { 
          Debug.WriteLine("Task could not be completed." + task.Exception.InnerException); 
         } 
        }, TaskContinuationOptions.OnlyOnFaulted); 

        upload.Wait(cancellationToken); 
       } 
      } 
      else 
      { 
       var blockIds = new List<string>(); 
       var index = 1; 
       long startPosition = 0; 
       long bytesUploaded = 0; 

       do 
       { 
        var bytesToRead = Math.Min(blockSize, bytesToUpload); 
        var blobContents = new byte[bytesToRead]; 

        using (var fileStream = new FileStream(fileData.LocalFileName, FileMode.Open)) 
        { 
         fileStream.Position = startPosition; 
         fileStream.Read(blobContents, 0, (int)bytesToRead); 
        } 

        var manualResetEvent = new ManualResetEvent(false); 
        var blockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(index.ToString("d6"))); 
        Debug.WriteLine($"Now uploading block # {index.ToString("d6")}"); 
        blockIds.Add(blockId); 
        var upload = blob.PutBlockAsync(blockId, new MemoryStream(blobContents), null); 

        upload.ContinueWith(task => 
        { 
         bytesUploaded += bytesToRead; 
         bytesToUpload -= bytesToRead; 
         startPosition += bytesToRead; 
         index++; 
         var percentComplete = (double)bytesUploaded/fileSize; 
         Debug.WriteLine($"Percent complete: {percentComplete.ToString("P")}"); 
         manualResetEvent.Set(); 
        }); 

        manualResetEvent.WaitOne(); 
       } while (bytesToUpload > 0); 

       Debug.WriteLine("Now committing block list."); 
       var putBlockList = blob.PutBlockListAsync(blockIds); 

       putBlockList.ContinueWith(task => 
       { 
        Debug.WriteLine("Blob uploaded completely."); 
       }); 

       putBlockList.Wait(); 
      } 

      File.Delete(fileData.LocalFileName); 
      return base.ExecutePostProcessingAsync(); 
     } 
    } 
} 

は、だから今まで、私は次の実装を持っています示唆している。この方法は、ファイルがAzureストレージに正常にアップロードされるという点で優れています。私は、このサービスへの呼び出しXMLHttpRequestを利用して(とprogressイベントをサブスクライブ)を作るときに、私は、インジケータは非常に迅速に100%に移動ご覧ください。アップロードに約5分のファイルが必要な場合、私のインジケータはわずか1秒で終了に移動します。したがって、おそらく問題はサーバーがクライアントにアップロードの進行状況を通知する方法にあります。これについて何か考えていますか?ありがとうございました。

================================アップデート1 ============私はあなたの進行状況インジケータがあるかもしれない

function uploadFile(file, index, uploadCompleted) { 
    var authData = localStorageService.get("authorizationData"); 
    var xhr = new XMLHttpRequest(); 

    xhr.upload.addEventListener("progress", function (event) { 
     fileUploadPercent = Math.floor((event.loaded/event.total) * 100); 
     console.log(fileUploadPercent + " %"); 
    }); 

    xhr.onreadystatechange = function (event) { 
     if (event.target.readyState === event.target.DONE) { 

      if (event.target.status !== 200) { 
      } else { 
       var parsedResponse = JSON.parse(event.target.response); 
       uploadCompleted(parsedResponse); 
      } 

     } 
    }; 

    xhr.open("post", uploadFileServiceUrl, true); 
    xhr.setRequestHeader("Authorization", "Bearer " + authData.token); 

    var data = new FormData(); 
    data.append("file-" + index, file); 

    xhr.send(data); 
} 
+0

Giorgios、あなたはどのように進捗イベントに登録しますか? –

答えて

6

サービスを呼び出すために使用するJavaScriptコードです=======================

急速に高速移動、それを待つことができるならば、それだけでクールの意志イムさえわからない、非同期型のAPIを作成するときに、私は、この前に遭遇したので

public async Task<HttpResponseMessage> UploadFile() 

であるかもしれませんSEだけのための非同期メソッドの、仕上げ即座にあなたの進行状況インジケータを理由、背景にあなたのAPIコールを終了(火災や忘れて)。 apiはすぐに応答を返しますが、サーバーのバックグラウンドで実際に終了します(待たれていない場合)。

親切、それだけで

public HttpResponseMessage UploadFile() 

作ってみると、また、これらのもの

var result = Request.Content.ReadAsMultipartAsync(streamProvider).Result; 
var upload = blob.UploadFromStreamAsync(fileStream, cancellationToken).Result; 

OR

var upload = await blob.UploadFromStreamAsync(fileStream, cancellationToken); 

を試してみてください、それが役に立てば幸い。

+0

ありがとうございます。あなたの提案には論理的な点がありますが、残念ながら結果は同じです。私はすべての呼び出しを同期して動作させることさえしようとしましたが、アップロードの進捗状況は実際の進捗と比較して決して正確ではありません。また、アップロードサービスを呼び出すために使用するJavaScript呼び出しを投稿に追加しました。ありがとうございました。 –

+0

あなたは応答で '.Result'を呼び出すべきではありません。あなたはデッドロックに陥るでしょう。代わりに、常にコードを待ってください。 – Cody

2

XMLHttpRequestの進行状況イベントの仕組みがわからない他の方法は、ProgressMessageHandlerを使用して要求のアップロードの進行状況を取得しています。クライアントに通知するために、キャッシュを使用して進行状況を保存し、クライアントから他のエンドポイントに現在の状態を要求するか、またはSignalRを使用してサーバからクライアントに進行状況を送信することができます。 :

//WebApiConfigRegister 
var progress = new ProgressMessageHandler(); 
progress.HttpSendProgress += HttpSendProgress; 
config.MessageHandlers.Add(progress); 
//End WebApiConfig Register 

    private static void HttpSendProgress(object sender, HttpProgressEventArgs e) 
    { 
     var request = sender as HttpRequestMessage; 
     //todo: check if request is not null 
     //Get an Id from the client or something like this to identify the request 
     var id = request.RequestUri.Query[0]; 
     var perc = e.ProgressPercentage; 
     var b = e.TotalBytes; 
     var bt = e.BytesTransferred; 
     Cache.InsertOrUpdate(id, perc); 
    } 

あなたはより多くのドキュメントon this MSDN blog post(「進行通知」セクションまでスクロールダウン)

また、あなたは、データチャンクに基づいて進捗状況を計算し、キャッシュ内の進行状況を保存し、通知することもできますを確認することができます上記と同じ方法でSomething like this solution

+0

SignalRの使用は私の考えでもありましたが、私は進捗状況の報告を避けるためにそれを避けたかったのです。しかし、それは私が持っている唯一の方法と思われる。ありがとうございました。 –

関連する問題