24

Nasza aplikacja ASP.NET MVC 3 działa na platformie Azure i używa Bloba jako magazynu plików. Mam część uploadu zorientowaną.Pobieranie plików Azure Blob w MVC3

Widok będzie miał nazwę pliku, który po kliknięciu spowoduje wyświetlenie ekranu pobierania pliku.

Czy ktoś może mi powiedzieć, jak to zrobić?

Odpowiedz

52

Dwie opcje naprawdę ... pierwsza polega na bezpośrednim przekierowaniu użytkownika bezpośrednio do obszaru typu blob (jeśli obiekty typu blob znajdują się w publicznym kontenerze). Że będzie wyglądać trochę jak:

return Redirect(container.GetBlobReference(name).Uri.AbsoluteUri); 

Jeśli plama jest w prywatnej kontenera, można użyć podpisu współdzielony dostęp i nie Przekierowanie jak w poprzednim przykładzie, czy można odczytać blob w swoim działaniu regulatora i dociśnij ją do klienta do pobrania:

Response.AddHeader("Content-Disposition", "attachment; filename=" + name); // force download 
container.GetBlobReference(name).DownloadToStream(Response.OutputStream); 
return new EmptyResult(); 
+0

Jest to prywatny obiekt typu blob, więc użyłem drugiej metody, którą opublikowałeś i działało dokładnie tak, jak chciałem. Dziękuję Ci bardzo! – James

+0

Chcę ukryć nazwę pliku od użytkownika (i umieścić moje własne), czy wiesz, jak to zrobić? – James

+2

Po prostu umieść cokolwiek chcesz w nagłówku Content-Disposition. – smarx

9

Oto wznawialną wersja (przydatne w przypadku dużych plików lub pozostawienie szukać wideo lub odtwarzania audio) z prywatnym dostępem blob:

public class AzureBlobStream : ActionResult 
{ 
    private string filename, containerName; 

    public AzureBlobStream(string containerName, string filename) 
    { 
     this.containerName = containerName; 
     this.filename = filename; 
     this.contentType = contentType; 
    } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     var response = context.HttpContext.Response; 
     var request = context.HttpContext.Request; 

     var connectionString = ConfigurationManager.ConnectionStrings["Storage"].ConnectionString; 
     var account = CloudStorageAccount.Parse(connectionString); 
     var client = account.CreateCloudBlobClient(); 
     var container = client.GetContainerReference(containerName); 
     var blob = container.GetBlockBlobReference(filename); 

     blob.FetchAttributes(); 
     var fileLength = blob.Properties.Length; 
     var fileExists = fileLength > 0; 
     var etag = blob.Properties.ETag; 

     var responseLength = fileLength; 
     var buffer = new byte[4096]; 
     var startIndex = 0; 

     //if the "If-Match" exists and is different to etag (or is equal to any "*" with no resource) then return 412 precondition failed 
     if (request.Headers["If-Match"] == "*" && !fileExists || 
      request.Headers["If-Match"] != null && request.Headers["If-Match"] != "*" && request.Headers["If-Match"] != etag) 
     { 
      response.StatusCode = (int)HttpStatusCode.PreconditionFailed; 
      return; 
     } 

     if (!fileExists) 
     { 
      response.StatusCode = (int)HttpStatusCode.NotFound; 
      return; 
     } 

     if (request.Headers["If-None-Match"] == etag) 
     { 
      response.StatusCode = (int)HttpStatusCode.NotModified; 
      return; 
     } 

     if (request.Headers["Range"] != null && (request.Headers["If-Range"] == null || request.Headers["IF-Range"] == etag)) 
     { 
      var match = Regex.Match(request.Headers["Range"], @"bytes=(\d*)-(\d*)"); 
      startIndex = Util.Parse<int>(match.Groups[1].Value); 
      responseLength = (Util.Parse<int?>(match.Groups[2].Value) + 1 ?? fileLength) - startIndex; 
      response.StatusCode = (int)HttpStatusCode.PartialContent; 
      response.Headers["Content-Range"] = "bytes " + startIndex + "-" + (startIndex + responseLength - 1) + "/" + fileLength; 
     } 

     response.Headers["Accept-Ranges"] = "bytes"; 
     response.Headers["Content-Length"] = responseLength.ToString(); 
     response.Cache.SetCacheability(HttpCacheability.Public); //required for etag output 
     response.Cache.SetETag(etag); //required for IE9 resumable downloads 
     response.ContentType = blob.Properties.ContentType; 

     blob.DownloadRangeToStream(response.OutputStream, startIndex, responseLength); 
    } 
} 

przykład:

Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); // force download 
return new AzureBlobStream(blobContainerName, filename); 
+0

Każdy pomysł, jak zmusić nagłówek 'Cache-Control' wyniku tak, aby był taki sam jak blob? – dlras2

+0

Co to jest 'Util.Parse'? –

8

zauważyliśmy, że pisząc do strumienia odpowiedzi od sposobu działania bałagan nagłówki HTTP. Brak niektórych oczekiwanych nagłówków, a inne nie są ustawione poprawnie.

Więc zamiast zapisywać do strumienia odpowiedzi, otrzymuję zawartość blob jako strumień i przekazuję go do metody Controller.File().

CloudBlockBlob blob = container.GetBlockBlobReference(blobName); 
Stream blobStream = blob.OpenRead(); 
return File(blobStream, blob.Properties.ContentType, "FileName.txt"); 
Powiązane problemy