visual tutorial studio ejemplo crear c# azure asp.net-web-api2 azure-storage-blobs

tutorial - web api json c#



API web: obtenga progreso al cargar en el almacenamiento de Azure (2)

su indicador de progreso podría moverse rápidamente rápido, podría ser debido a

public async Task<HttpResponseMessage> UploadFile()

He encontrado esto antes, al crear una API de tipo asíncrono, ni siquiera estoy seguro de que pueda esperar, simplemente por supuesto solo finalizará tu llamada de API en el fondo, por lo que tu indicador de progreso finalizará instantáneamente, debido al método asíncrono (dispara y olvida). la API inmediatamente le dará una respuesta, pero terminará en el fondo del servidor (si no está esperando).

por favor amablemente intenta hacerlo solo

public HttpResponseMessage UploadFile()

y también prueba estos

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

O

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

Espero eso ayude.

La tarea que quiero lograr es crear un servicio de API web para subir un archivo al almacenamiento de Azure. Al mismo tiempo, me gustaría tener un indicador de progreso que refleje el progreso de carga real. Después de investigar y estudiar descubrí dos cosas importantes:

Primero, tengo que dividir el archivo manualmente en fragmentos y subirlos de forma asincrónica utilizando el método PutBlockAsync de Microsoft.WindowsAzure.Storage.dll .

En segundo lugar, es que tengo que recibir el archivo en mi servicio de API web en modo de transmisión y no en modo de almacenamiento intermedio.

Hasta ahora tengo la siguiente implementación:

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(); } } }

También habilité el modo de transmisión por secuencias como lo sugiere esta publicación de blog. Este enfoque funciona muy bien, en términos de que el archivo se carga correctamente en el almacenamiento de Azure. Luego, cuando realizo una llamada a este servicio haciendo uso de XMLHttpRequest (y me suscribo al evento de progreso) veo que el indicador se mueve al 100% muy rápidamente. Si un archivo de 5MB necesita alrededor de 1 minuto para cargarlo, mi indicador se mueve hasta el final en solo 1 segundo. Entonces, probablemente el problema reside en la forma en que el servidor informa al cliente sobre el progreso de la carga. ¿Alguna idea sobre esto? Gracias.

============================= Actualización 1 ================ ===================

Ese es el código JavaScript que uso para llamar al servicio

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); }


Otra forma de lograr lo que desea (no entiendo cómo funciona el evento de progreso de XMLHttpRequest) es usar ProgressMessageHandler para obtener el progreso de la carga en la solicitud. Luego, para notificar al cliente, puede usar algún caché para almacenar el progreso, y desde el cliente solicitar el estado actual en otro punto final, o usar SignalR para enviar el progreso del servidor al cliente

Algo como:

//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); }

Puede consultar más documentación en esta publicación de blog de MSDN (Desplácese hacia abajo a la sección "Notificaciones de progreso")

Además, puede calcular el progreso en función de los fragmentos de datos, almacenar el progreso en un caché y notificar de la misma manera que arriba. Algo como esta solución