从客户端浏览器直传文件到Storage
关于上传文件到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的更多相关文章
- 直传文件到Azure Storage的Blob服务中
(此文章同时发表在本人微信公众号“dotNET每日精华文章”,欢迎右边二维码来关注.) 题记:为了庆祝获得微信公众号赞赏功能,忙里抽闲分享一下最近工作的一点心得:如何直接从浏览器中上传文件到Azure ...
- HTML5+flash打造兼容各浏览器的文件上传方案
上一篇文章介绍了HTML5版的文件上传插件,相比flash,采用HTML5的新技术无疑可以提升程序的加载速度.但是在目前的情况看来,HTML5的特性支持度不高,插件的可用性范围确实比较窄.例如,我在插 ...
- asp.net 客户端上传文件全路径获取方法
asp.net 获取客户端上传文件全路径方法: eg:F:\test\1.doc 基于浏览器安全问题,浏览器将屏蔽获取客户端文件全路径的方法,只能获取到文件的文件名,如果需要获取全路径则需要另想其他 ...
- asp.net 浏览器下载文件的四种方式
// 方法一:TransmitFile实现下载 protected void Button1_Click(object sender, EventArgs e) { Response.ContentT ...
- 页面加载异常 清除浏览器静态文件 js css 缓存 js动态加载js css文件,可以配置文件后辍,防止浏览器缓存
js清除浏览器缓存的几种方法 - 兔老霸夏 - 博客园 https://www.cnblogs.com/Mr-Rocker/p/6031096.html js清除浏览器缓存的几种方法 一.CSS和 ...
- Axis 生成客户端client stub文件
[转自] http://blog.csdn.net/qiao000_000/article/details/5568442 开发前,有个同事先给我们不熟悉Web Service的程序员进行了一些培训, ...
- 利用Beef劫持客户端浏览器
利用Beef劫持客户端浏览器 环境: 1.Kali(使用beef生成恶意代码,IP:192.168.114.140) 2.一台web服务器(留言板存在XSS跨站脚本漏洞,IP:192.168.11 ...
- 使用Beef劫持客户端浏览器并进一步使用Beef+msf拿客户端shell
环境: 1.Kali(使用beef生成恶意代码,IP:192.168.114.140) 2.一台web服务器(留言板存在XSS跨站脚本漏洞,IP:192.168.114.204) 3. 客户端(用于访 ...
- 利用form.submit提交表单导出文件到客户端浏览器, 提示下载!
本来是想利用ajax提交json数据到服务端, 让服务端生成一个excel文件并提示客户端浏览器下载的. 但是搞了很久发现ajax方式是无法触发浏览器弹出文件下载的. 网上很多的方案都是说利用form ...
随机推荐
- html5 canvas多个图像旋转
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- [转载]win7休眠后网络断开怎么办?如何设置?
http://jingyan.baidu.com/article/8065f87fc87d0423312498af.html 有时会遇到在Windows7系统休眠模式下会自动断开网络连接,唤醒系统也是 ...
- gulp.js 的安装以及使用
首先:电脑需要安装 Node.js 一个大绿色的安装按钮,点击就可以. 但还是推荐,点击download选中一款适合电脑配置的版本. Node安装过程,就是下一步 and 下一步~~ 测试手否安装成功 ...
- AngularJS -- 代码实例
整理书籍内容(QQ:283125476 发布者:M [重在分享,有建议请联系->QQ号]) ng-change 当文本输入字段中内容发生了变化,就会改变equation.x的值: <bod ...
- SpringSecurity csrf验证忽略某些请求
前几天项目中遇到springSecurity问题,研究了大半天,掉进了csrf的坑,先认识一下csrf CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟 ...
- HTML5 CSS Reset
最近在学习HTML和CSS,发现一个不错的模板,放于此处. /* html5doctor.com Reset Stylesheet v1.6.1 Last Updated: 2010-09-17 Au ...
- 数据库索引和SQL语句使用经验
1.如果检索数据量超过30%的表中记录数,使用索引将没有显著的效率提高 2.在特定情况下,使用索引也许会比全表扫描慢,但这是同一个数量级上的差距:而通常情况下,使用索引比全表扫描要快几倍乃至几千倍! ...
- How to fix Eclipse Tomcat startup timeout in 45 seconds?
命题:怎么修复 Eclipse 里启动 Tomcat 时, 默认启动超时时间为 45 秒的问题? 在 Eclipse 的 J2EE 项目里启动 Tomcat 来调试 Servlet 之类的,断点以后, ...
- Oracle学习笔记:使用replace、regexp_replace实现字符替换、姓名脱敏
在数据库中难免会遇到需要对数据进行脱敏的操作,无论是姓名,还是身份证号. 最近遇到一个需求,需要对姓名进行脱敏: 姓名长度为2,替换为姓+*: 姓名长度为3,替换中间字符为*: 姓名长度为4,替换第3 ...
- The last access date is not changed even after reading the file on Windows 7
https://superuser.com/questions/251263/the-last-access-date-is-not-changed-even-after-reading-the-fi ...