关于上传文件到Azure Storage没有什么可讲的,不论我们使用哪种平台、语言,上传流程都如下图所示:

从上图我们可以了解到从客户端上传文件到Storage,是需要先将文件上传到应用服务上,然后再通过应用服务器上的后端代码将文件上传到Storage,在这里应用服务器就是起到一个代理的作用。

当然,这是最常规的做法,但是确有一个致命的弱点,那就是我们上传文件到Storage中,是要占用应用服务器的计算、网络、IO等资源的,这对于一些对性能要求很高的应用场景是很难接受的。

那么,我们是否有解决方案?答案是肯定的,我们可以跳过应用服务器从客户端将文件直传到Storage中,那么我们如何来构建这套解决方案呢?这就是我们下面要讲的。

我们先来看下直传文件到Storage的流程图:

这里特别有几点需要注意的地方:

1、  不能使用存储账号和存储密钥来验证身份进行文件上传,因为这会在客户端暴露存储账号和存储密钥等敏感信息。

2、  Storage默认是不能跨域访问的,所以我们需要将应用服务器所在域加入到Storage的跨域规则中。

3、  通过上图,我们可以看到客户端一共发送两次http请求,第一次从应用服务器获取Shared Access Signature(具有写权限),再带着获取到的SAS将文件从客户端上传到Storage。

大致流程和思路已经讲清楚了,那么下面我们就来看看用代码是如何实现的。

Index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
<meta charset="utf-8" />
<title>直传文件到Storage</title>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/uploadfile.js"></script>
</head>
<body>
<input type="file" id="file" name="file" /> <div id="output">
<strong>File properties:</strong>
<br />
<p>
Name: <span id="fileName"></span>
</p>
<p>
File size: <span id="fileSize"></span>bytes.
</p>
<p>
File type: <span id="fileType"></span>
</p>
<p>
<input type="button" value="Upload File" id="uploadFile" />
</p>
</div>
</body>
</html>

updatefile.js

(function ($) {
var reader = null,
requestUri = null,
selectedFile = null,
blobName = null; function sendAjax(url, dataToSend, beforeSendFunction, successFunction) {
$.ajax({
url: url,
type: "PUT",
data: dataToSend,
processData: false,
beforeSend: beforeSendFunction,
tryCount: 0,
success: successFunction, });
} function readerOnLoadEnd(evt) {
if (evt.target.readyState === FileReader.DONE) {
var uri = requestUri,
requestData = new Uint8Array(evt.target.result);
sendAjax(uri, requestData,
function (xhr) {
xhr.setRequestHeader("x-ms-blob-type", "BlockBlob");
xhr.setRequestHeader("x-ms-blob-content-type", getContentType(blobName));
}, function (data, status) {
alert("upload success.");
});
}
}; function handleFileSelect(e) {
selectedFile = e.target.files[0];
blobName = selectedFile.name;
var fileSize = selectedFile.size;
$('#output').show();
$('#fileName').text(blobName);
$('#fileSize').text(fileSize);
$('#fileType').text(selectedFile.type); $.get('{获取Blob SAS的接口地址}', { 'blobName': blobName }, function (data) {
console.log(data);
requestUri = data;
});
} function startUpload() {
$("#uploadFile").prop('disabled', true);
$("#file").prop('disabled', true);
var slice = selectedFile.slice(0, selectedFile.size);
reader.readAsArrayBuffer(slice);
} function getContentType(blobName) {
var ext = blobName.substr(blobName.lastIndexOf('.'));
var contentType = 'application/octet-stream';
switch (ext) {
case ".txt":
contentType = "text/plain";
break;
case ".png":
contentType = "image/png";
break;
case ".zip":
contentType = "application/zip";
break;
case ".pptx":
contentType = "application/vnd.ms-powerpoint";
break;
case ".docx":
contentType = "application/msword";
break;
case ".pdf":
contentType = "application/pdf";
break;
case ".jpg":
case ".jpeg":
contentType = "image/jpeg";
break;
case ".html":
contentType = "text/html";
break;
case ".js":
contentType = "application/x-javascript";
break;
case ".css":
contentType = "text/css";
break;
case ".gif":
contentType = "image/gif";
break;
case ".ico":
contentType = "image/x-icon";
break;
case ".mp4":
contentType = "video/mpeg4";
break;
case ".mp3":
contentType = "audio/mp3";
break;
default:
break;
}
return contentType;
} $(function () {
$('#output').hide();
if (window.File && window.FileReader && window.FileList && window.Blob) {
reader = new FileReader();
reader.onloadend = readerOnLoadEnd;
} else {
alert('The File APIs are not fully supported in this browser.');
$('#file').prop('disabled', true);
return;
}
$('#file').bind('change', handleFileSelect);
//上传文件
$('#uploadFile').bind('click', startUpload);
});
}(jQuery))

后端提供的Shared Access Signature接口代码

private string _StorageConnectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
private string _AccountName = ConfigurationManager.AppSettings["AccountName"];
private string _AccountKey = ConfigurationManager.AppSettings["AccountKey"];
private string _ContainerName = ConfigurationManager.AppSettings["ContainerName"]; [HttpGet]
public string GetRequestStorageUri(string blobName)
{
if (string.IsNullOrEmpty(blobName))
{
throw new ArgumentNullException();
}
if (string.IsNullOrEmpty(_ContainerName)) {
throw new NullReferenceException("container is null or empty");
} CloudBlockBlob blockBlob = GetCloudBlockBlob(_ContainerName, blobName);
string sas = GetBlobSharedAccessSignature(blockBlob);
string requestUri = string.Format("https://{0}.blob.core.chinacloudapi.cn/{1}/{2}{3}", _AccountName, _ContainerName, blobName, sas);
return requestUri;
} private CloudBlockBlob GetCloudBlockBlob(string containerName,string blobName)
{
if (string.IsNullOrEmpty(containerName) || string.IsNullOrEmpty(blobName))
{
throw new ArgumentNullException();
}
if (string.IsNullOrEmpty(_StorageConnectionString))
{
throw new NullReferenceException("storage connection string is null or empty");
}
CloudStorageAccount account = CloudStorageAccount.Parse(_StorageConnectionString);
CloudBlobClient blobClient = account.CreateCloudBlobClient();
ServiceProperties serviceProperties = blobClient.GetServiceProperties();
serviceProperties.Cors.CorsRules.Clear();
serviceProperties.Cors.CorsRules.Add(new CorsRule
{
AllowedOrigins = new List<string> { "{应用服务器所在域}" },
AllowedMethods = CorsHttpMethods.Get| CorsHttpMethods.Put| CorsHttpMethods.Post| CorsHttpMethods.Delete,
AllowedHeaders = new List<string> { "x-ms-*", "content-type", "accept" },
MaxAgeInSeconds = * *
});
blobClient.SetServiceProperties(serviceProperties);
CloudBlobContainer container = blobClient.GetContainerReference(containerName);
container.CreateIfNotExists();
CloudBlockBlob blockBlob = container.GetBlockBlobReference(blobName);
return blockBlob;
} private string GetBlobSharedAccessSignature(CloudBlob blob) {
if (blob == null)
{
throw new ArgumentNullException("blob is null");
}
return blob.GetSharedAccessSignature(new SharedAccessBlobPolicy {
Permissions = SharedAccessBlobPermissions.Add| SharedAccessBlobPermissions.Create| SharedAccessBlobPermissions.Delete| SharedAccessBlobPermissions.List| SharedAccessBlobPermissions.Read| SharedAccessBlobPermissions.Write,
SharedAccessStartTime = DateTime.UtcNow,
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes()
});
}

从客户端浏览器直传文件到Storage的更多相关文章

  1. 直传文件到Azure Storage的Blob服务中

    (此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注.) 题记:为了庆祝获得微信公众号赞赏功能,忙里抽闲分享一下最近工作的一点心得:如何直接从浏览器中上传文件到Azure ...

  2. HTML5+flash打造兼容各浏览器的文件上传方案

    上一篇文章介绍了HTML5版的文件上传插件,相比flash,采用HTML5的新技术无疑可以提升程序的加载速度.但是在目前的情况看来,HTML5的特性支持度不高,插件的可用性范围确实比较窄.例如,我在插 ...

  3. asp.net 客户端上传文件全路径获取方法

    asp.net  获取客户端上传文件全路径方法: eg:F:\test\1.doc 基于浏览器安全问题,浏览器将屏蔽获取客户端文件全路径的方法,只能获取到文件的文件名,如果需要获取全路径则需要另想其他 ...

  4. asp.net 浏览器下载文件的四种方式

    // 方法一:TransmitFile实现下载 protected void Button1_Click(object sender, EventArgs e) { Response.ContentT ...

  5. 页面加载异常 清除浏览器静态文件 js css 缓存 js动态加载js css文件,可以配置文件后辍,防止浏览器缓存

    js清除浏览器缓存的几种方法 - 兔老霸夏 - 博客园 https://www.cnblogs.com/Mr-Rocker/p/6031096.html js清除浏览器缓存的几种方法   一.CSS和 ...

  6. Axis 生成客户端client stub文件

    [转自] http://blog.csdn.net/qiao000_000/article/details/5568442 开发前,有个同事先给我们不熟悉Web Service的程序员进行了一些培训, ...

  7. 利用Beef劫持客户端浏览器

    利用Beef劫持客户端浏览器   环境: 1.Kali(使用beef生成恶意代码,IP:192.168.114.140) 2.一台web服务器(留言板存在XSS跨站脚本漏洞,IP:192.168.11 ...

  8. 使用Beef劫持客户端浏览器并进一步使用Beef+msf拿客户端shell

    环境: 1.Kali(使用beef生成恶意代码,IP:192.168.114.140) 2.一台web服务器(留言板存在XSS跨站脚本漏洞,IP:192.168.114.204) 3. 客户端(用于访 ...

  9. 利用form.submit提交表单导出文件到客户端浏览器, 提示下载!

    本来是想利用ajax提交json数据到服务端, 让服务端生成一个excel文件并提示客户端浏览器下载的. 但是搞了很久发现ajax方式是无法触发浏览器弹出文件下载的. 网上很多的方案都是说利用form ...

随机推荐

  1. scrapy 简单防封

    设置爬取间隔 setting.py from random import random DOWNLOAD_DELAY = random()* ps:此次的爬取间隔,在读取seeting文件确定,并非每 ...

  2. UVALive 6176 Faulhaber's Triangle

    题目链接 http://acm.sdibt.edu.cn/vjudge/ojFiles/uvalive/pdf/61/6177.pdf 题意是  给定一个数n,代表着一共有n个人,且他们的身高从1到n ...

  3. HDU 1863 畅通工程 最下生成树问题

    题目描述:给出图,要你求是否存在最小生成树,如果存在,要求输出最小权值和,如果不存在,输出? 解题报告:又是一个最裸的克鲁斯卡尔,并且要判断是否存在最小生成树的问题.废话不多说,给个短代码: #inc ...

  4. vs-code 配置

    vs-code 快键键 命令面板 ctrl+shift+p vs-code 相关插件 AutoFileName Chinese (Simplified) Language Pack for Visua ...

  5. mysqlbinlog 查看mysql bin 日志 mysqlbinlog: unknown variable 'default-character-set=utf8'

    mysqlbinlog  mysql-bin.000036 | less 查询包含几个字段的语句: mysqlbinlog mysql-bin.000036| egrep '(201103061000 ...

  6. 【Alsa】播放声音和录音详细流程

    linux中,无论是oss还是alsa体系,录音和放音的数据流必须分析清楚.先分析alsa驱动层,然后关联到alsa库层和应用层. 二,链接分析: 1)链路一 usr/src/linux-source ...

  7. Linux 管道

    管道命令 " | ",竖线符号代表的就是管道符 管道是一种两个进程间进行单向通信的机制.因为管道传递数据的单向性,所以又称为半双工管道. 介绍: 管道可以根据一组命令按照数据流向的 ...

  8. pixel像素基础

    地址:http://www.imooc.com/video/9564 dp(安卓),pt(iphone)是物理像素 ppi是由物理像素确定的 一英寸内有多少个像素渲染,ppi越高,图片越清晰 1px ...

  9. Java多态概述

    多态 所谓多态,实际上就是一个对象的多种状态: 下面例子中,Tiger可以看做Tiger,也可以看做Animal Cat  可以看做Cat,也可以看做Animal Dog 可以看做Dog,也可以看做A ...

  10. SqlServer中查看索引的使用情况

    --查看数据库索引的使用情况 select db_name(database_id) as N'TOPK_TO_DEV', --库名 object_name(a.object_id) as N'Top ...