24

我們的ASP.NET MVC 3應用程序在Azure上運行,並使用Blob作爲文件存儲。我已經找到了上傳部分。下載MVC3中的Azure Blob文件

該視圖將具有文件名,單擊該文件名時將提示文件下載屏幕出現。

誰能告訴我怎麼去做這個?

回答

52

真的有兩個選擇......第一個是直接將用戶重定向到blob(如果blob在公共容器中)。這看起來有點像:

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

如果斑是在私人的容器,你既可以使用共享訪問簽名並做redirection(重定向)像前面的例子,或者你可以在你的控制器動作讀取blob和推下來給客戶端下載:

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

這是一個私人blob,所以我用你發佈的第二種方法,它的工作原理是如何我想要的。非常感謝你! – James

+0

我想隱藏用戶的文件名(並把我自己的)你知道如何做到這一點? – James

+2

只需在Content-Disposition標題中放入任何你想要的東西。 – smarx

9

下面是一個可恢復版本(大文件有用或允許尋求播放視頻或音頻)的私人數據訪問有:

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

示例:

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

任何想法如何強制結果的'緩存控制'頭與blob的相同? – dlras2

+0

什麼是'Util.Parse'? –

8

我注意到從action方法寫入響應流會混淆HTTP標頭。一些預期的標題不見了,其他的設置不正確。

因此,不是寫入響應流,而是將blob內容作爲流獲取,並將其傳遞給Controller.File()方法。

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