本文主要涉及两个概念:

  • 阿里云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. BZOJ4724 [POI2017]Podzielno

    4724: [POI2017]Podzielno Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 77  Solved: 37[Submit][Stat ...

  2. Quartz2D之绘制一个简单的机器猫

    学习iOS有一段时间了,在博客园也默默的潜水了两个月,见识了很多大神,收获不少. 今天整理笔记,发现忘记的不少,我感觉需要及时的整理一下了,同时也把做的小东西贴上来和大家分享一下. 最近学习了Quar ...

  3. [BZOJ 3681]Ariettad

    终于是打完了 CH 上的数据结构专场了…… 不过看样子还有一套 5555 传送门: http://ch.ezoj.tk/contest/CH%20Round%20%2351%20-%20Shinrei ...

  4. NewQuant正式在Github发布

    Github上的NewQuant https://github.com/xuruilong100/NewQuant NewQuant简介: NewQuant是一个小型开源C++库,可以解决数据分析.金 ...

  5. Linux 基础笔记

    1    vim /etc/inittab     linux配置文件,这个文件负责设置init初始化程序初始化脚本在哪里;2    passwd    修改密码2.1    passwd xxx   ...

  6. maven项目 启动报错:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

    tomcat在发布项目的时候没有同时发布maven依赖所添加的jar包,所以找不到解决方法:设置一下eclipse:项目 -> properties -> Deployment Assem ...

  7. 10天学会phpWeChat——第二天:hello world!我的第一个功能模块

    今天我们开始进入<10天学会phpWeChat>系列教程的第二天:创建我的第一个hello world! 功能模块. 1.登录后台,进入 系统设置--自定义模块,如图: 自定义模块参数说明 ...

  8. 调试新技能get - powershell

    1. win+r -> powershell_ise 2. debug-process 3. type processname....WITHOUT ".exe"

  9. 【学】AngularJS日记(4)- 过滤器的使用

    过滤器: 过滤器中的 |json,可以使原来的json数据输出时按照换行的样式 过滤器 | limitTo:2可以截取字符串或者数组的前2位 过滤器| orderBy 可以进行排序,加入json里的k ...

  10. 学习opencv

    图像缩放 cv::Mat src_img = cv::imread(); ; cv::Mat dst_img1; cv::Mat dst_img2(src_img.rows*0.5, src_img. ...