如何从Azure Blob存储下载文件到浏览器

sta*_*eth 27 c# asp.net-mvc azure azure-storage azure-storage-blobs

我已经成功列出了可用文件,但我需要知道如何将该文件传递给浏览器以供用户下载而无需将其保存到服务器

以下是我获取文件列表的方法

var azureConnectionString = CloudConfigurationManager.GetSetting("AzureBackupStorageConnectString");
var containerName = ConfigurationManager.AppSettings["FmAzureBackupStorageContainer"];
if (azureConnectionString == null || containerName == null)
    return null;

CloudStorageAccount backupStorageAccount = CloudStorageAccount.Parse(azureConnectionString);
var backupBlobClient = backupStorageAccount.CreateCloudBlobClient();
var container = backupBlobClient.GetContainerReference(containerName); 
var blobs = container.ListBlobs(useFlatBlobListing: true);
var downloads = blobs.Select(blob => blob.Uri.Segments.Last()).ToList();
Run Code Online (Sandbox Code Playgroud)

Dav*_*gon 50

虽然blob内容可以通过Web服务器流式传输,并通过浏览器传输给最终用户,但此解决方案会将负载放在Web服务器上,包括cpu和NIC.

另一种方法是向最终用户提供要下载的所需blob的uri,他们可以在html内容中单击该ub.例如https://myaccount.blob.core.windows.net/mycontainer/myblob.ext.

这个问题是如果内容是私有的,因为除非使用公共blob,否则上面的uri将无法工作.为此,您可以创建共享访问签名(或服务器存储的策略),然后生成附加到uri的散列查询字符串.这个新的uri将在给定的时间长度内有效(例如10分钟).

这是为blob创建SAS的一个小例子:

var sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5);
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10);
sasConstraints.Permissions = SharedAccessBlobPermissions.Read;

var sasBlobToken = blob.GetSharedAccessSignature(sasConstraints);

return blob.Uri + sasBlobToken;
Run Code Online (Sandbox Code Playgroud)

请注意,开始时间设置为过去的几分钟.这是为了处理时钟漂移.这是我从中获取/修改此代码示例的完整教程.

通过使用直接blob访问,您将完全绕过VM/Web角色实例/网站实例(减少服务器负载),并让最终用户直接从blob存储中提取blob内容.您仍然可以使用您的Web应用程序来处理权限,决定要传递的内容等.但是......这使您可以直接链接到blob资源,而不是通过Web服务器传输它们.

  • 有关代客钥匙模式的更多信息,请访问:https://msdn.microsoft.com/en-us/library/dn568102.aspx (2认同)

sta*_*eth 9

用户单击文件后,服​​务器将对此进行响应

var blob = container.GetBlobReferenceFromServer(option);

var memStream = new MemoryStream();
blob.DownloadToStream(memStream);

Response.ContentType = blob.Properties.ContentType;
Response.AddHeader("Content-Disposition", "Attachment;filename=" + option);
Response.AddHeader("Content-Length", blob.Properties.Length.ToString());
Response.BinaryWrite(memStream.ToArray());
Run Code Online (Sandbox Code Playgroud)

非常感谢Dhananjay Kumar的解决方案

  • 因此,通过这样做,您确实意识到blob的整个内容将通过您的服务器,对吧?也就是说,blob的内容将从blob存储传输到您的VM /网站/ Web角色实例,然后传递给最终用户,通过IIS/OWIN /等? (3认同)
  • 我发布了另一个答案. (3认同)
  • 你会推荐什么?我无法让我的最终用户访问整个存储,因此 Azure 存储资源管理器将不起作用。 (2认同)
  • 我发现这很慢,因为第一个字节不会进入浏览器,直到最后一个字节来自 blob 存储。使用 Andy 的两行答案可以减少内存开销和 10 毫秒的延迟! (2认同)

And*_*ndy 8

如果您使用 ASP.NET(核心),您可以将内容流式传输到浏览器,而无需将文件保存在服务器上,并且使用 FileStreamResult 是 IActionResult 将是更优雅的解决方案。

var stream = await blob.OpenReadAsync();
return File(stream, blob.Properties.ContentType, option);
Run Code Online (Sandbox Code Playgroud)

  • 这有点神奇,但没有下载整个 blob,只有范围,就像 blob 存储知道你只想要文件的一部分,并且只将那部分传输到主机,然后主机只转发它微小的缓冲区。 (3认同)
  • 您需要在 SAS 令牌上设置一个很小的到期日期,以防止正在对通过接入点的内容进行基本记录的任何人不立即下载任何内容,并且您不能将其设置得太短,因为浏览器需要时间来执行往返并将请求直接返回到 blob 存储。至少如果您在 https 下执行 POST,他们将需要解密消息以获取必要的密钥以获取 blob,我认为更安全,但我不是 SAS 令牌保护方面的专家。必须有一种安全的方法来做到这一点,但 GET 不是。 (3认同)
  • 如果您不想冒 SAS 令牌被盗的风险,这是最好的解决方案,毕竟它们只是在查询字符串中,因此即使使用 https 也没有加密。此外,如果您正在流式传输安全视频,请在返回 File 对象之前在 File 对象上设置 EnableRangeProcessing,它会让浏览器跳过视频而无需下载整个文件! (2认同)
  • @DanielBailey 我不同意,这将使整个 blob 不必要地下载到您的应用程序,从而消耗服务器的带宽,也使过程变慢。您可以像 David 的回答一样设置 SAS 令牌的到期日期,而且您不能只是窃取令牌并读取任何内容,附加到 Uri 的令牌仅适用于该 blob,这意味着拥有该 Uri 的任何人都可以使用来自将执行上面 Andy 代码的应用程序的 Uri。 (2认同)