【问题标题】:Azure Blob storage: DownloadToByteArray VS DownloadToStreamAzure Blob 存储:DownloadToByteArray VS DownloadToStream
【发布时间】:2014-08-10 08:11:36
【问题描述】:

我一直在使用 Azure Blob 存储服务来保存/恢复要在 Azure 网页中托管的网页上下文中的文件。

在学习过程中,我提出了两种解决方案;第一个基本上使用DownloadToStream,它的作用相同,但使用FileStream。在这种情况下,我必须先将文件写入服务器,然后再将其返回给用户。

public static Stream GetFileContent(string fileName, HttpContextBase context)
{
      CloudBlobContainer container = GetBlobContainer();    
      CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);                                       
      Stream fileStream = new FileStream(
          context.Server.MapPath("~/App_Data/files/" + fileName), FileMode.Create);   
      blockBlob.DownloadToStream(fileStream);
      fileStream.Close();    
      return File.OpenRead(context.Server.MapPath("~/App_Data/files/" + fileName));
}

public ActionResult Download(string fileName)
{
    byte[] fileContent = MyFileContext.GetFileContent(fileName);
    return File(fileContent, "application/zip", fileName);        
}

另一方面,我使用 DownloadToByteArray 函数将 Blob 的内容写入以 Blob 文件大小初始化的字节数组中。

public static byte[] GetFileContent(string fileName)
{
    CloudBlobContainer container = GetBlobContainer();           
    CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
    blockBlob.FetchAttributes();
    long fileByteLength = blockBlob.Properties.Length;
    byte[] fileContent = new byte[fileByteLength];
    for (int i = 0; i < fileByteLength; i++)
    {
        fileContent[i] = 0x20;
    }
    blockBlob.DownloadToByteArray(fileContent,0);
    return fileContent;
}

public ActionResult Download(string fileName)
{   
   byte[] fileContent = MyFileContext.GetFileStream(fileName);
   return File(fileContent, "application/zip", fileName);
}

当我查看这两个选项时,我发现第一个选项需要在服务器磁盘中创建一个文件,而第二个选项将 Blob 中的数据存储在一个字节数组中消耗内存。在我的特殊情况下,我将处理 ~150 MB 的文件大小。

鉴于情况(环境、文件大小...),您认为哪种方法最好?

【问题讨论】:

  • 您的目标是始终下载用户计算机上的文件吗?或者换句话说,您是想在将这些数据流式传输到用户浏览器之前对其进行处理,还是直接从 Azure 存储下载用户计算机上的文件?
  • 只是为了不忽略某些东西:DownloadToStream 需要 StreamFileStream 并不是唯一的流类型。
  • @GauravMantri 是的,这个想法只是下载文件而不对其执行任何操作。
  • 查看这个类似的问题:stackoverflow.com/questions/6752000/… 给出了一种解决方案,即文件直接发送到响应输出流而无需中间保存到磁盘。或者,如果 blob 是公开的并且您的业务逻辑允许,您可以随时提供指向该 blob 的直接 http 链接。
  • @Julen fileContent[i] = 0x20; 背后的原因是什么?

标签: c# azure


【解决方案1】:

您可以直接从 Blob 存储下载它,而不是通过您的服务器流式传输 Blob。我的答案是建立在史蒂夫的回复之上:Downloading Azure Blob files in MVC3。要直接从存储中下载 blob,您可以使用 Shared Access Signature (SAS)。最近 Azure 存储引入了一项增强功能,允许您在 SAS 中指定 Content-Disposition 标头。请参阅此修改后的代码。

    public static string GetDownloadLink(string fileName)
    {
        CloudBlobContainer container = GetBlobContainer();
        CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);
        //Create an ad-hoc Shared Access Policy with read permissions which will expire in 12 hours
        SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy()
        {
            Permissions = SharedAccessBlobPermissions.Read,
            SharedAccessExpiryTime = DateTime.UtcNow.AddHours(12),
        };
        //Set content-disposition header for force download
        SharedAccessBlobHeaders headers = new SharedAccessBlobHeaders()
        {
            ContentDisposition = string.Format("attachment;filename=\"{0}\"", fileName),
        };
        var sasToken = blockBlob.GetSharedAccessSignature(policy, headers);
        return blockBlob.Uri.AbsoluteUri + sasToken;
    }

    public ActionResult Download(string fileName)
    {
        var sasUrl = GetDownloadLink(fileName);
        //Redirect to SAS URL ... file will now be downloaded directly from blob storage.
        Redirect(sasUrl);
    }

【讨论】:

  • 谢谢!它很好用!一个棘手的问题,应该被认为是打开权限的潜在安全漏洞?在我的解决方案中,我设置了一个 30 秒的窗口。
  • 我的示例中的共享到期时间仅用于演示目的。您应该根据您认为用户需要下载文件的时间来设置它。如果您认为您的用户可以在 30 秒内下载文件,您可以将其设置为该值。您可能想阅读这篇博文以了解 SAS 最佳实践:blogs.msdn.com/b/windowsazurestorage/archive/2012/06/12/…。 HTH。
  • 请务必注意,SAS 令牌超时指示何时不再可能开始下载。只要在 SAS 令牌到期之前开始下载,下载时间可能会比这长几分钟,并且下载正常。
  • @PeterJ 是正确的。 1-2 秒的超时应该可以解决问题,但请注意,如果您的服务器时间不同步,您可能会遇到问题。为防止这种情况发生,请确保将服务器系统时钟与原子世界时钟服务同步。
  • 这种方式不适合在html文档中设置图片src,因为这样每次都会创建一个新的下载URI,浏览器可能无法缓存它
【解决方案2】:

Stream 的好处是您可以在下载时逐个处理比特,而不是构建一个大字节[],然后对整个内容进行操作。您使用 Stream 并没有真正获得好处,因为您正在写入文件,然后将该完整文件读入内存。流 API 的一个很好的用途是将下载流直接通过管道传输到请求的响应流,如这里的答案Downloading Azure Blob files in MVC3

【讨论】:

  • 谢谢罗伯特。这个解决方案更适合我的项目。
【解决方案3】:

如果您打算使用 DownloadToBytesArray(异步与否),则必须先获取 blob 属性以获取字节数组的初始大小。

如果您将使用 DownloadToStream,则不必这样做。 这是一个保存的对 blob 存储的 HTTP 调用,如果我没记错的话,FetchAttributes() 将作为 HTTP HEAD 请求执行,这将被视为正常事务(换句话说,这将花费你一些钱)。

【讨论】:

  • 获取属性不会增加太多开销,但绝对值得一提。
猜你喜欢
  • 2017-04-28
  • 2019-08-29
  • 2017-11-10
  • 2012-03-02
  • 2016-03-04
  • 2014-09-19
  • 2019-10-25
  • 1970-01-01
  • 2016-06-16
相关资源
最近更新 更多