本文主要涉及两个概念:

  • 阿里云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. 刷题ING...

    我用codeVS刷题.. 努力准备!!

  2. Java单例模式(转载)

    原文地址:1:http://www.cnblogs.com/hupp/p/4487521; 2:http://cantellow.javaeye.com/blog/838473 先罗列五种单例模式的实 ...

  3. Unity全视角跟随鼠标右键转换视角实现——研究笔记

    using UnityEngine; using System.Collections; public class CameraMove : MonoBehaviour { public Transf ...

  4. Unity扩展编辑器学习笔记--从路径下找到拥有某个组件类型的预设

    public static List<T> GetAssetsWithScript<T>(string path) where T:MonoBehaviour { T tmp; ...

  5. 【IOS学习】1.IOS框架

    1.框架概述 iOS的系统架构分为四个层次:核心操作系统层(Core OS layer).核心服务层(Core Services layer).媒体层(Media layer)和可触摸层(Cocoa ...

  6. 面试题五 数组中出现次数超过一半的数字 时间为O(n)

    也就是说 该数字出现的次数比其他所有数字出现次数的和还要多. 因此可以保存两个值,一个数字,一个次数. 遍历时 1.如果数字相同,count++ 2.如果count == 0 count = 1 nu ...

  7. ListView的属性及方法详解

    本文转载于:http://blog.csdn.net/vector_yi/article/details/23195411 近期在重新学习Android控件知识,目前进行到ListView,感觉这是一 ...

  8. windows2003 DHCP中批处理绑定IP与MAC

    最近正在实施Windows Server 2008 R2 DHCP服务器部署,要求把员工的IP地址和MAC以及姓名完成在DHCP服务器上的绑定,使用的系统是windows2003-x64,要添加的用户 ...

  9. toggle()方法和hove()方法

    toggle()语法结构: toggle(fn1,fn2,fn3,....fnN); 第一次单击元素,触发第一个元素,再次单击触发第二个元素,如果有更多元素,依次触发,直到最后一个元素,随后单击反复对 ...

  10. VS高效开发快捷键

    Ctrl + Tab 标签切换 Ctrl + '-'向后导航 Ctrl + Shift+'-'向前导航 Ctrl +Shift +空格  提示函数参数 Ctrl +F4 退出本标签 Ctrl+F 查找 ...