本文主要涉及两个概念:

  • 阿里云OSS:对象存储(Object Storage Service,简称OSS),是阿里云对外提供的海量、安全和高可靠的云存储服务。
  • bootstrap-fileinput:An enhanced HTML 5 file input for Bootstrap 3.x with file preview for various files, offers multiple selection, and more.

客服称OSS不限制上下行流量,而且不占用ECS带宽,所以将静态文件存放在OSS是个不错的选择。除了放置站点引用的资源文件外,主要用于用户文件的上传。阿里云提供了Web端直传方式,文件不经过站点服务器,不影响站点的性能和带宽。真正撸代码时,遇到了不少坑,而官方文档秉承阿里一贯的程序员风格,天马行空毫不连贯,博主东拼西凑才理顺各细节,关键点记录如下,供需要的朋友参考。


阿里官方没有提供.NET的demo,找到Post Object,该文档描述了提交的各参数。进行Post操作要求对bucket有写权限,如果bucket为public-read-write,可以不上传签名信息,否则要求对该操作进行签名验证。与Put操作不同,Post操作使用AccessKeySecret对policy进行签名计算出签名字符串作为Signature表单域的值,OSS会验证该值从而判断签名的合法性(不需要遵循用户签名验证(Authentication)的规定,另外在header或url中加上签名)。policy又是啥呢?Post请求的policy表单域用于验证请求的合法性。 policy为一段经过UTF-8和base64编码的JSON文本,声明了Post请求必须满足的条件(文件大小、一些http头等,还有该条件的过期时间)。虽然对于public-read-write的bucket上传时,post表单域为可选项(其它情况,因为需要签名验证,所以是必选项),也强烈建议使用该域来限制Post请求。

so,第一步,构造Post条件,对于不同bucket,可以有不同的条件,可以写在web.config中,如果经常变动,也可以写在数据库中方便管理。

然后,给前端提供一个获取policy的接口,代码就不贴了,使用官方提供的SDK,很简单。这里假设接口名称为GetPolicyForOSSUpload,返回结果是:

Data = new ModelForOSSUpload
{
AccessKeyId = aliyunconfig.AccessKeyId,
PolicyBase64 = encodedPolicy,
Signature = signature,
Expiration = expiration
}

注意,expiration已经被用于构造PolicyBase64了,这里又单独给个字段返回,是给前端用的,在有效时间内,不用再调该接口,能减轻服务器压力一点是一点。

前端js是这样的:

 _getUploadPreSetting: function (bucketKey, policyName) {
var presetting = {}, error = {}, self = this;
var inner_getUploadPreSetting = function () {
var now = new Date();
if (!presetting.Expiration || presetting.Expiration < now) {
presetting = {};
error = {};
$.ajax({
url: self._ossOpts.uploadPreSettingUrl,
type: 'GET',
async: false,
data: { bucketKey: bucketKey, policyName: policyName },
success: function (result) {
if (result.IsSucceed) {
presetting = result.Data;
//应付/Date(1460817323032)/这种奇葩格式
presetting.Expiration = eval('new ' + (presetting.Expiration.replace(/\//g, '')));
}
else {
error = result;
}
},
error: function (xhr, msg) {
error = { IsSucceed: false, Message: msg };
}
});
}
return presetting || error;
};
return inner_getUploadPreSetting;
}

此处用了闭包,这是因为不同的policy,我们以bucketKey和policyName进行区分(看贴出来的web.config片段),如果一个页面需要多个policy,每个policy都要对应一个变量,数量不定的情况下,我们也不能硬编码,同时为了让它们互不影响,就采用了闭包的方式。调用如下:

var getUploadPreSetting = this._getUploadPreSetting(this._ossOpts.bucketKey, this._ossOpts.policyName);
var presetting = getUploadPreSetting();

policy的部分到此为止,下面我们来看下bootstrap-fileinput这个组件,并对它进行一点点改造,以便于符合OSS的上传规则。


除了文件数据,提交时还要附加OSS要求的一些表单域,比如刚才讲的policy、Signature,还可以给每个文件加上cache-Control、callback(上传成功后,OSS回调我们的接口地址)等。我们在初始化bootstrap-fileinput(下称fileinput)时可以传入一个uploadExtraData配置项(函数或对象),在上传每个文件时,fileinput会调用uploadExtraData,将获取到的数据附加到文件数据后一起上传。然而这就存在一个问题了,OSS文档中说“文件或文本内容,必须是表单中的最后一个域。”,否则会返回“The bucket POST must contain the specified 'key'. If it is specified, please check the order of the fields”的错误信息。

题外话:关于表单域顺序的说明,以前文档中并没有,还是博主在一次工单提交和差点投诉后才加上去的,当时很奇怪难道没人遇到过,还是说这样用OSS的人不多?另外在和客服扯皮的同时,博主也上网搜了一把,发现AWS的用户竟然也遇到类似问题,请看 InvalidArgumentBucket POST must contain a field named 'key'. If it is specified, please check the order of the fields.keyB,连返回的错误消息都大同小异,博主大胆猜测,阿里云和AWS的数据存储用的是同一套组件,还是开源的,博主知识浅陋,有知道的朋友说一声。

于是只能去看fileinput的源码了,幸好它是开源的,我们只需要改动一下_uploadSingle函数的末尾:

self._uploadExtra(previewId, i); //新加
formdata.append(self.uploadFileAttr, files[i], self.filenames[i]); //文件数据
formdata.append('file_id', i);
self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, previewId, i, true); //也要改造下_ajaxSubmit,传入true表示额外数据不再附加

_ajaxSubmit也改造了下,圆圈内为新加:

fileinput改造完毕,so easy!那么当上传成功后,我们一般都是要对应用数据做一些处理,比如更新数据库中的url地址什么的,我们可以获取响应信息,然后再调用相应的后台接口,OSS也提供了直接回调的方式,后者能传递更多关于文件的信息,并且在构造callback时可自定义参数,因此更灵活,方便后台回调接口的数据处理,具体可看Callback。


下例,前端js:

callback:{
"callbackUrl":GlobalConfig.siteurl+'Merchandise/SetMerchandisePicUrl',
"callbackBody":"merchandiseid="+vm.merchandise.BasicInfo.ID+"&picname=${object}"
}

后端回调接口:

 [AllowAnonymous]
public async Task<ActionResult> SetMerchandisePicUrl(int merchandiseid, string picname)
{
var aliyunconfig = MvcApplication.AliyunConfig;
var bucket = aliyunconfig.OSS.Buckets["merchandise_pictures"];
var picUri = $"http://{ bucket.Value}.{aliyunconfig.OSS.ImgEndpoint}/{picname}";
await MerchandiseContext.AppendPicUri(merchandiseid, picUri);
return this.Json(new
{
initialPreview = new string[] { picUri },
initialPreviewConfig = new List<object> {
new {
caption = picname,
//width ="160px", //并没有什么用
url =Url.Action("RemoveMerchandisePicUrl"),
extra = new {picUri= picUri,merchandiseid = merchandiseid}
}
}
});
}

接口允许匿名,便于OSS调用,为了防止恶意调用,最好进行签名校验。fileinput根据返回结果判断是否上传成功(没有error属性表示上传成功),若成功且结果里有initialPreview[和initialPreviewConfig]等属性,则将这些属性应用到对应文件显示。特别注意initialPreviewConfig中的url和extra属性,表示删除该文件时调用的接口和传递的参数。

另外,若我们不指定回掉地址,那么OSS默认会返回空文档(在上传成功的情况下),若需要让其返回官方文档中提到的PostResponse,需要指定success_action_status为201。


其它参考资料:

理解DOMString、Document、FormData、Blob、File、ArrayBuffer数据类型

Web 前沿——HTML5 Form Data 对象的使用

转载请注明本文出处:http://www.cnblogs.com/newton/p/6066020.html

Upload files to aliyunOSS with bootstrap-fileinput的更多相关文章

  1. 结合bootstrap fileinput插件和Bootstrap-table表格插件,实现文件上传、预览、提交的导入Excel数据操作流程

    1.bootstrap-fileinpu的简单介绍 在前面的随笔,我介绍了Bootstrap-table表格插件的具体项目应用过程,本篇随笔介绍另外一个Bootstrap FieInput插件的使用, ...

  2. bootstrap fileinput 使用记录

    第一次使用bootstrap fileinput碰到了许多坑,做下记录 需求 本次使用bootstrap fileinput文件上传组件,主要用来上传和预览图片.作为一个后台管理功能,为某个表的某个字 ...

  3. Bootstrap fileinput.js,最好用的文件上传组件

    本篇介绍如何使用bootstrap fileinput.js(最好用的文件上传组件)来进行图片的展示,上传,包括springMVC后端文件保存. 一.demo   二.插件引入 <link ty ...

  4. JS文件上传神器bootstrap fileinput详解

    Bootstrap FileInput插件功能如此强大,完全没有理由不去使用,但是国内很少能找到本插件完整的使用方法,于是本人去其官网翻译了一下英文说明文档放在这里供英文不好的同学勉强查阅.另外附上一 ...

  5. ***Bootstrap FileInput插件的使用经验汇总

    插件下载地址: https://github.com/kartik-v/bootstrap-fileinput/ 官方DEMO查看: http://plugins.krajee.com/file-ba ...

  6. bootstrap fileinput插件使用感悟

    bootstrap fileinput 的填坑感悟              这个插件在demo的网站地址http://plugins.krajee.com/file-preview-icons-de ...

  7. Bootstrap FileInput中文API整理

    这段时间做项目用到bootstrap fileinput插件上传文件,在用的过程中,网上能查到的api都不是很全,所以想着整理一份比较详细的文档,方便自己今后使用,也希望能给大家带来帮助,如有错误,希 ...

  8. Bootstrap FileInput中文API文档

    Bootstrap FileInput中文API整理 这段时间做项目用到bootstrap fileinput插件上传文件,在用的过程中,网上能查到的api都不是很全,所以想着整理一份比较详细的文档, ...

  9. bootstrap fileinput +springmvc图片上传-krajee

    引入的文件 <link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/4.4.9/css/filei ...

随机推荐

  1. cocos2d-x 帧循环不严谨造成场景切换卡顿

    最近在用cocos2d-x做引导界面,2dx版本是2.2.3,场景切换加上了效果,所有资源都已经使用texturepacker打包预加载,但是在实际运行调试中,场景切换相当卡顿. 各种纠结后,无意中将 ...

  2. jsp xml servlet

    什么都懂一点,什么都不精通!!

  3. dfs序

    dfs序比较重要的性质:一棵子树的所有节点在dfs序里是连续一段,主要就是利用这个性质来解题 题型一:对某个点X权值加上一个数W,查询某个子树X里所有点权值和. 解:列出dfs序,实现修改一个数,查询 ...

  4. InfoPi简介、试用链接、资料索引

    InfoPi的定位是“个人信息收集服务器”. 用户可以灵活地定义信息的来源,比如从网页抓取感兴趣的信息.订阅博客.从温度传感器读取数据,等等. 然后,把收集到的信息用web的方式展示出来. 可以把In ...

  5. JVM 参数分配

    http://stackoverflow.com/questions/41216388/java-jvm-parameter-xms-doesnt-take-effect-immediately It ...

  6. DBA-mysql-字符集

    查看支持的字符集:show charset; 查看现使用字符集:status; 1.在[mysqld]下添加 default-character-set=utf8 (mysql 5.1版本) char ...

  7. git常见命令

    总结自己的Git常用命令 使用git也有一段时间了,把自己常用的命令用自己的描述记录起来,方便自己备忘也方便其他人参考. 目录: 最基本的命令: git clone 拷贝并跟踪远程的master分支. ...

  8. D3树状图给指定特性的边特别显示颜色

    D3作为前端图形显示的利器,功能之强,对底层技术细节要求相对比较多. 有一点,就是要理解其基本的数据和节点的匹配规则架构,即enter,update和exit原理,我前面的D3基础篇中有介绍过,不明白 ...

  9. Spark 1.6以后的内存管理机制

     Spark 内部管理机制 Spark的内存管理自从1.6开始改变.老的内存管理实现自自staticMemoryManager类,然而现在它被称之为"legacy". " ...

  10. bug_ _ _常见的bug??

    ======= 7    Failure [INSTALL_FAILED_INVALID_APK] 执行  adb install -r test.apk.时出现错误  Failure [INSTAL ...