C# Azure Blob存储:DownloadToByteArray与DownloadToStream

C# Azure Blob存储:DownloadToByteArray与DownloadToStream,c#,azure,C#,Azure,我一直在使用Azure Blob存储服务来保存/恢复将托管在Azure网页中的网页上下文中的文件 在学习过程中,我提出了两种解决方案;第一个基本上使用了DownloadToStream,这与FileStream的功能相同。在这种情况下,在将文件返回给用户之前,我必须在服务器中写入该文件 public static Stream GetFileContent(string fileName, HttpContextBase context) { CloudBlobContainer c

我一直在使用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);
}
公共静态字节[]GetFileContent(字符串文件名)
{
CloudBlobContainer容器=GetBlobContainer();
CloudBlockBlob blockBlob=container.getBlockBlockBlobReference(文件名);
blockBlob.FetchAttributes();
long filebytellength=blockBlob.Properties.Length;
byte[]fileContent=新字节[filebytellength];
for(int i=0;i
当我查看这两个选项时,我看到第一个选项需要在服务器的磁盘中创建一个文件,而第二个选项将Blob中的数据存储在占用内存的字节数组中。在我的特殊情况下,我将处理约150MB的文件大小


在这种情况下(环境、文件大小……),您认为哪种方法最好?

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

而不是通过服务器传输blob,您可以直接从blob存储下载它。我的答案建立在史蒂夫的回答之上。要直接从存储器下载blob,可以使用
共享访问签名(SAS)
。最近,Azure Storage引入了一项增强功能,它允许您在SAS中指定
内容处置
头。请参阅此修改后的代码

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

如果您计划使用下载的字节数组(异步或非异步),则必须首先获取blob属性以获得字节数组的初始大小

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

您的目标始终是在用户的计算机上下载文件吗?换句话说,您想在将此数据流式传输到用户浏览器之前对其进行处理,还是可以从Azure存储直接在用户的计算机上下载该文件?为了避免忽略某些内容:
DownloadToStream
需要
,但
FileStream
不是唯一的流类型。@GauravMantri是的,我们的想法是在不执行任何操作的情况下加载文件。@CyberDude谢谢,我只是认为如果我处理文件,FileStream应该是合适的,或者有更好的选择?请看这个类似的问题:给出了一个解决方案,文件直接发送到响应输出流,而无需中间保存到磁盘。或者,如果blob是公共的,并且您的业务逻辑允许,那么您可以始终向blob发送一个直接的http链接。谢谢!很好用!有一个问题很棘手,是否应将其视为打开权限的潜在安全漏洞?在我的解决方案中,我设置了一个30秒的窗口。在我的示例中,共享到期时间仅用于演示目的。你应该根据你认为你的用户需要下载文件的时间来设置它。如果您认为,您的用户可以在30秒内下载该文件,您可以将其设置为该值。您可能想阅读以下有关SAS最佳实践的博文:。HTH。需要注意的是,SAS令牌超时指示何时无法开始下载。只要下载在SAS令牌过期之前开始,下载可能需要几分钟的时间,而且下载会很好。@PeterJ是正确的。超时1-2秒就可以了,但是请注意,如果您的服务器时间不同步,您可能会遇到问题。为了防止这种情况,请确保您的服务器系统时钟与原子世界时钟服务同步。谢谢Robert。这个解决方案更适合我的项目。获取属性不会增加很多开销,但绝对值得调用。