关于上传文件到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. windows下nodejs服务器的安装与配置

    1下载安装 download from the link: https://nodejs.org/en/ windows下的安装直接运行exe,略过-- 注:由于用户权限的问题,最好将nodejs安装 ...

  2. python_ssh连接

    首先下载paramikopip install paramiko查看并启动ssh服务service ssh status 添加用户:useradd -d /home/zet zetpasswd zet ...

  3. Windows运行命令

    winver---------检查Windows版本 wmimgmt.msc----打开windows管理体系结构 wupdmgr--------windows更新程序 winver--------- ...

  4. DM816X 实现 USB HID Gadget 鼠标键盘功能【转】

    转自:https://my.oschina.net/renyongke/blog/410695 开发环境: 平台: DM8168 内核 :linux 2.6.32 RDK:DVRRDK_04.00.0 ...

  5. Linux内核源码分析--内核启动之(1)zImage自解压过程(Linux-3.0 ARMv7) 【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-4938388.html 研究内核源码和内核运行原理的时候,很总要的一点是要了解内核的初始情况,也就是要了解内 ...

  6. c#按字符串中的数字排序问题

    在.net 的framewrok框架中提供的排序方法中,如string.sort() 或ArrayList.Sort()方法.这两个方法对字符串排序时,如果字符串中含有数字,则不会按数字大小排序.如: ...

  7. Python常用time处理

    #coding=utf-8 # python2.7工具语句 from sys import version_info import time import datetime if version_in ...

  8. sonar Lint ----code bad smell

    类名注释报黄: 去掉这段黄做法:alt+enter 本文参考: http://www.cnblogs.com/xxoome/p/6677170.html

  9. Java ListIterator(迭代器)

    LIstIterator是一个更加强大的Iterator的子类型,它只能用于各种List类的访问,尽管Iterator只能向前移动,但是ListIterator可以双向移动,它还可以产生相对于迭代器在 ...

  10. springMVC:将controller中数据传递到jsp页面

    1> 将方法的返回值该为ModelAndView在返回时,将数据存储在ModelAndView对象中如: newModelAndView("/WEBINF/jsp/showData.j ...