直传文件到Azure Storage的Blob服务中
(此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注。)
题记:为了庆祝获得微信公众号赞赏功能,忙里抽闲分享一下最近工作的一点心得:如何直接从浏览器中上传文件到Azure Storage的Blob服务中。
为什么
如果你的Web应用程序利用了云存储(比如Azure Storage)来存储用户的资源文件(比如图片、视频等等)。通常的做法,是用户访问你的Web前端,上传文件到你的Web后端应用,然后在后端程序中使用云存储的SDK把文件再转传到云存储中。架构如下图所示:
这种模式下,虽然简单方便。但是,由于上传文件的过程,需要以Web后端程序作为代理,如果上传文件巨大频繁,会给后端程序的托管服务或托管服务器造成较大运算压力和流量压力。所以,还有一种模式,是让用户直接在浏览器中把文件上传给云存储服务。我所熟知的云平台(Azure ,AWS,Aliyun)都提供了类似的特性,只是实现方式或名称上有所不同。另外,在这种方式下,为了保证安全性,一般不会直接把云存储的访问Key暴露给Web前端,所以都会提供一种折中方式,让你可以生成限时失效权限有限的共享访问Key,把这个共享Key给到前端来获得访问能力。在Azure中,这个特性称之为共享访问签章(Shared Access Signatures,SAS),而整个架构就变为下面这样:
在这个架构中,你先从后端服务器获得SAS Url,然后直接上传文件给Azure Storage,上传文件成功后,如果需要再把一些文件元数据传递给后端服务器(其实Azure的文件也可以额外保存元数据的,你自己都可以不保存元数据)。其实这种架构不仅可以运用于文件存储服务,在Azure中还可以在前端直接访问Azure Storage Table、Queue等服务。关于SAS模型,微软官方的文档《Shared Access Signatures, Part 1: Understanding the SAS Model》(http://t.cn/R4OQona)讲的很清楚,上面的图就是引自这篇文章。
当然,在很多时候,我们是需要混用这两种模式的,在需要更多安全控制和流量可控的情况,使用代理转传模式;在安全可隔离流量不可控的情况下,使用直传模式。我自己的实践当中,也是两种模式混用,在需要用户上传文件到公共存储账号的时候,使用代理模式,在用户上传文件到用户独有存储账号的时候,使用直传模式。
由于我当前使用的云平台是Azure,所以下面演示的代码也是基于Azure Storage SDK的。
获取SAS访问地址
根据Azure的文档《Shared Access Signatures, Part 2: Create and use a SAS with Blob storage》(http://t.cn/R4OQeBd)所述,获取SAS其实也非常简单。
首先实例化CloudStorageAccount、CloudBlobClient和CloudBlobContainer,如下:
//Parse the connection string and return a reference to the storage account.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString")); //Create the blob client object.
CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); //Get a reference to a container to use for the sample code, and create it if it does not exist.
CloudBlobContainer container = blobClient.GetContainerReference("sascontainer");
container.CreateIfNotExists();
然后创建一个临时的策略,调用CloudBlobContainer的GetSharedAccessSignature方法来生成访问token,如下:
//Set the expiry time and permissions for the container.
//In this case no start time is specified, so the shared access signature becomes valid immediately.
SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
sasConstraints.SharedAccessExpiryTime = DateTime.UtcNow.AddHours(24);
sasConstraints.Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.List; //Generate the shared access signature on the container, setting the constraints directly on the signature.
string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);
所生成的token实际就是一个包含有多个策略规则的查询字符串,然后把这个token通过Web后端的一个Api调用(当然是验证用户权限后)传递给Web前端。前端为要上传的文件构造这样一个Url:存储容器的Uri+要上传的文件名(包括所在文件夹)+SAS Token,然后把文件流HTTP PUT到这个Url就可以实现上传。
上述代码生成的是一个存储容器的SAS Url,其实也可以针对一个Blob对象生成SAS Url。
另外需要注意的是,你也可以在存储容器上设定一个固定的共享访问策略(而非临时设置在SAS token上),这样你可以更加方便和系统的控制SAS的有效性。详情可参考上面提到的文档。
设置跨域策略
直传文件实际上调用的是Azure Storage REST API,在最初的时候,Azure Storage是不支持跨域访问的(CORS),在这种情况下,只有设置Storage容器的自定义域和Web应用程序的域一致。不过现在有了CORS的支持,就很简单了,通过下面的方法可以设置CORS(:
public static async Task AddCorsRuleAsync(CloudBlobClient blobClient)
{
//First get the service properties from storage to ensure we're not adding the same CORS rule again.
var serviceProperties =await blobClient.GetServicePropertiesAsync();
var corsSettings = serviceProperties.Cors;
var corsRule = corsSettings.CorsRules.FirstOrDefault(
o => o.AllowedOrigins.Contains("http://localhost:3904"));//设置你自己的服务器地址
if (corsRule == null)
{
//Add a new rule.
corsRule = new CorsRule()
{
AllowedHeaders = new List<string> { "x-ms-*", "content-type", "accept" },
AllowedMethods = CorsHttpMethods.Put,//Since we'll only be calling Put Blob, let's just allow PUT verb
AllowedOrigins = new List<string> { "http://localhost:3904" },//This is the URL of our application.
MaxAgeInSeconds = 1 * 60 * 60,//Let the browswer cache it for an hour
};
corsSettings.CorsRules.Add(corsRule);
//Save the rule
await blobClient.SetServicePropertiesAsync(serviceProperties);
}
}
对于CORS更加详细的解释,可以参考Azure Storage的MSDN Blog上的这篇文章《Windows Azure Storage: Introducing CORS》和Gaurav Mantri的这篇博文《Windows Azure Storage and Cross-Origin Resource Sharing (CORS) – Lets Have Some Fun》
集成WebUploader
在获得SAS并设置了CORS之后,最后的事情,就是如何把文件提交到SAS Url了。虽然我们可以采用Ajax或HTML5原生的方式来发送文件(如上两篇文章中所演示的),但是使用一个现有的文件上传组件,应该是最方便(对用户也是如此)的方式了。
我尝试了把Baidu WebUploader集成,实现了多图片上传。在集成的过程,一些注意的地方有:
- 初始化uploader对象的时候,不要设置server属性,因为server地址需要动态获取(要获得SAS Url),且每个文件的SAS Url不一样(因为文件名不一样)
- 我是先预先获取SAS的token,然后在uploadStart事件中为每个文件生成元数据信息,和各自的server地址
- 在uploadBeforeSend事件中,来配置Azure所需的header信息
- 在uploadSuccess事件中,把文件的元数据传递给后端服务器
具体的代码可以查看我分享的代码片段:http://git.oschina.net/ike/codes/7edc84bio2zplhunyxvkr
最后,如果这篇文章对你有用,欢迎打赏。
直传文件到Azure Storage的Blob服务中的更多相关文章
- Azure Blob Storage 基本用法 -- Azure Storage 之 Blob
Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob.Queue.File 和 Table. 笔者在<Azure Table storage ...
- 【Azure Redis 缓存】Azure Cache for Redis服务中,除开放端口6379,6380外,对13000,13001,15000,15001 为什么也是开放的呢?
问题描述 在使用安全检测工具对Azure Redis服务端口进行扫描时,发现Redis对外开放了13001, 13000,15000,15001端口.非常不理解的是,在门户上只开放了6379,6380 ...
- [New Portal]Windows Azure Storage (14) 使用Azure Blob的PutBlock方法,实现文件的分块、离线上传
<Windows Azure Platform 系列文章目录> 相关内容 Windows Azure Platform (二十二) Windows Azure Storage Servic ...
- Windows Azure Storage (19) 再谈Azure Block Blob和Page Blob
<Windows Azure Platform 系列文章目录> 请读者在参考本文之前,预习相关背景知识:Windows Azure Storage (1) Windows Azure St ...
- 从客户端浏览器直传文件到Storage
关于上传文件到Azure Storage没有什么可讲的,不论我们使用哪种平台.语言,上传流程都如下图所示: 从上图我们可以了解到从客户端上传文件到Storage,是需要先将文件上传到应用服务上,然后再 ...
- Azure系列2.1 —— com.microsoft.azure.storage.blob
网上azure的资料较少,尤其是API,全是英文的,中文资料更是少之又少.这次由于公司项目需要使用Azure,所以对Azure的一些学习心得做下笔记,文中不正确地方请大家指正. Azure Blob ...
- Azure File Storage 基本用法 -- Azure Storage 之 File
Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob.Queue.File 和 Table. 笔者在<Azure Blob Storage 基 ...
- Azure Table storage 基本用法 -- Azure Storage 之 Table
Azure Storage 是微软 Azure 云提供的云端存储解决方案,当前支持的存储类型有 Blob.Queue.File 和 Table,其中的 Table 就是本文的主角 Azure Tabl ...
- Microsoft Azure Storage架构分析
Microsoft云存储服务分为两个部分,SQL Azure和Azure Storage.云存储系统的可扩展性和功能不可兼得,必须牺牲一定的关系数据库功能换取可扩展性.Microsoft实现云存储的思 ...
随机推荐
- [20160731]read a file and print it on the screen
//read a file and print it on the screen import java.io.*; public class MyPrintStreamTest2{ public s ...
- Frog Jump
A frog is crossing a river. The river is divided into x units and at each unit there may or may not ...
- 4个http常用的content type
转的: http://www.aikaiyuan.com/6324.html HTTP/1.1 协议规定的 HTTP 请求方法有 OPTIONS.GET.HEAD.POST.PUT.DELETE.TR ...
- 配置tomcat的虚拟路径
配置tomcat的虚拟路径有两个地方需要配置,以eclipse为例: ①在tomcat的server.xml中的host节点内添加 <Context path="/meims/user ...
- winrt 真正的绑定任意命令
下载codeplex上的winrttrigger开源控件 Winrt.Tirgger.EventTrigger trigger = new Winrt.Tirgger.EventTrigger(); ...
- 深入浅出JMS(一)——JMS简介
如果手机只能进行实时通话,没有留言和短信功能会怎么样?一个电话打过来,正好没有来得及接上,那么这个电话要传递的信息肯定就收不到了.为什么不能先将信息存下来,当用户需要查看信息的时候再去获得信息呢?伴随 ...
- 【python】类变量和对象变量
来源:http://www.cnblogs.com/gtarcoder/p/5005897.html python是一种解释性的语言,任何变量可以在使用的时候才声明以及定义,也可以在程序运行的任何位置 ...
- 【python】list,dict赋值不要用等号,要用extend,update
如果有一个list,我们用连等号的方式赋值 c = d = [1], 则当c改变时,d同样会改变.字典同理 正确做法应该是: d = [1] c = [1] 或者 d = [1] c.extend(d ...
- 如何获取Iframe的页面控件的值
有时候我们在页面需要使用iframe的方法来引用另一个页面,虽然个人建议不要使用iframe哈,但是有些时候是没得法了才使用的咯,那么在使用的过程中也会遇到某一些问题,我将自己遇到的问题记录下来方便以 ...
- .NET微信公众号开发-2.0创建自定义菜单
一.前言 开发之前,我们需要阅读官方的接口说明文档,不得不吐槽一下,微信的这个官方文档真的很烂,但是,为了开发我们需要的功能,我们也不得不去看这些文档. 接口文档地址:http://mp.weixin ...