24

Ứng dụng ASP.NET MVC 3 của chúng tôi đang chạy trên Azure và sử dụng Blob làm lưu trữ tệp. Tôi có phần tải lên đã tìm ra.Tải xuống tệp Azure Blob trong MVC3

Chế độ xem sẽ có Tên tệp, khi được nhấp vào sẽ nhắc màn hình tải xuống tệp xuất hiện.

Có ai có thể cho tôi biết cách thực hiện việc này không?

Trả lời

52

Hai tùy chọn thực sự ... đầu tiên là chỉ chuyển hướng người dùng đến blob trực tiếp (nếu các đốm màu nằm trong vùng chứa công cộng). Điều đó sẽ trông giống như:

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

Nếu blob là trong một container tin, bạn có thể hoặc là sử dụng một Chữ ký Chia sẻ kết nối và làm chuyển hướng giống như ví dụ trước đó, hoặc bạn có thể đọc các blob trong hành động điều khiển của bạn và đẩy nó xuống cho khách hàng như là một download:

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

Đây là một blob riêng nên tôi đã sử dụng phương pháp thứ hai mà bạn đăng và nó hoạt động chính xác theo cách tôi muốn. Cảm ơn nhiều! – James

+0

Tôi muốn ẩn tên tệp khỏi người dùng (và tự đặt mình vào) bạn có biết cách thực hiện việc này không? – James

+2

Chỉ cần đặt bất kỳ thứ gì bạn muốn vào tiêu đề Nội dung Bố trí. – smarx

9

Dưới đây là một phiên bản resumable (hữu dụng cho các tập tin lớn hoặc cho phép tìm kiếm trong video hoặc phát lại âm thanh) tiếp cận blob tin:

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

Ví dụ:

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

Bất kỳ ý tưởng làm thế nào để buộc tiêu đề 'Cache-Control' của kết quả giống như của blob? – dlras2

+0

'Util.Parse' là gì? –

8

Tôi nhận thấy rằng việc ghi vào luồng phản hồi từ phương thức hành động sẽ làm lộn xộn tiêu đề HTTP. Một số tiêu đề dự kiến ​​bị thiếu và các tiêu đề khác không được đặt chính xác.

Vì vậy, thay vì ghi vào luồng phản hồi, tôi nhận nội dung blob dưới dạng luồng và chuyển nó sang phương thức Controller.File().

CloudBlockBlob blob = container.GetBlockBlobReference(blobName); 
Stream blobStream = blob.OpenRead(); 
return File(blobStream, blob.Properties.ContentType, "FileName.txt"); 
Các vấn đề liên quan