netcore3.1增加阿里云OSS云存储服务
问题描述:由于最近的项目访问量有点大,决定部署到两台服务器,做负载的同时问题也发现了,之前的程序附件上传是上传到程序根目录的,由于做了负载,网站访问的资源就得看运气了,多刷几次才能访问的到,于是乎干脆把资源文件都放到oss上面。
解决方案:先说一下整体思路,前台是jquery的,页面加载获取osstoken,有效期是10分钟,获取时候传个参数,后台根据参数判断文件存放位置,返回加密的token,前台拿到token就能给阿里云oss上传附件了。阿里云文档还是比较健全的,找了几个开源的源码关于oss上传,照猫画虎的来了一遍,慢慢摸索出了一些经验,首先阿里有oss的sdk包(Aliyun.OSS.SDK.NetCore,当前是2.9.1版本),引用到项目中,添加服务类
public static class ALiOSSHelper { private static readonly ILog FileLog = LogManager.GetLogger("NETCoreRepository", typeof(ALiOSSHelper)); private const string AccessKeyID = "XXX"; private const string AccessKeySecret = "XXX"; private const string Host = "http://XXX.oss-cn-beijing.aliyuncs.com"; private const string BucketName = "XXX"; public static ALiOSSTokenModel GetToken(int fileType) { var TargetDir = "FruitsCMS"; switch (fileType) { //资讯图片 case 1: TargetDir += "/NewsPic"; break; //轮播图 case 2: TargetDir += "/ScrollPic"; break; //友情链接Logo case 3: TargetDir += "/LinkPic"; break; default: TargetDir += "/UploadImage"; break; } OssClient client = new OssClient(Host, AccessKeyID, AccessKeySecret); //密钥过期时间为10分钟 var expiration = DateTime.Now.AddMinutes(10); var policyConds = new PolicyConditions(); policyConds.AddConditionItem("bucket", BucketName); policyConds.AddConditionItem(MatchMode.StartWith, PolicyConditions.CondKey, TargetDir); policyConds.AddConditionItem(MatchMode.StartWith, "x-oss-meta-tag", "dummy_etag"); //限制传输文件大小10M policyConds.AddConditionItem(PolicyConditions.CondContentLengthRange, 1, 10240000); var postPolicy = client.GeneratePostPolicy(expiration, policyConds); var encPolicy = Convert.ToBase64String(Encoding.UTF8.GetBytes(postPolicy)); var signature = ComputeSignature(AccessKeySecret, encPolicy); var tempRet = new ALiOSSTokenModel { key = TargetDir, bucket = BucketName, OSSAccessKeyId = AccessKeyID, policy = encPolicy, Signature = signature }; FileLog.Info("返回阿里云OSSToken信息:" + tempRet.ToJson()); return tempRet; } private static string ComputeSignature(string key, string data) { using (var algorithm = KeyedHashAlgorithm.Create("HmacSHA1".ToUpperInvariant())) { algorithm.Key = Encoding.UTF8.GetBytes(key.ToCharArray()); return Convert.ToBase64String( algorithm.ComputeHash(Encoding.UTF8.GetBytes(data.ToCharArray()))); } } }
返回给前台的对象模型 public class ALiOSSTokenModel { public string key { get; set; } public string bucket { get; set; } public string OSSAccessKeyId { get; set; } public string policy { get; set; } public string Signature { get; set; } }
最小改动原则,在页面底部增加一个单独的上传文件隐藏form,接口返回模型赋值即可
<form id="form2" action="http://XXX-cn-beijing.aliyuncs.com" method="POST" enctype="multipart/form-data"> <input type="hidden" id="key" name="key" /> <input type="hidden" id="bucket" name="bucket" /> <input type="hidden" id="x-oss-meta-tag" name="x-oss-meta-tag" value="dummy_etag_xxx" /> <input type="hidden" id="OSSAccessKeyId" name="OSSAccessKeyId" /> <input type="hidden" id="policy" name="policy" /> <input type="hidden" id="Signature" name="Signature" /> <input type="file" id="file" name="file" accept="image/*" style="display:none;"> </form>
function loadOssTokenInfo() { $.ajax({ url: "/Upload/GetOSSToken?fileType=3", dataType: "json", async: false, success: function (data) { $("#form2").formSerialize(data); } }); }
function initControl() { $("#Status").bindSelect(); $("#file").change(function () { var f = document.getElementById("file").files; if (f.length == 0) { $("#PicAddr").val(""); } else { var tempFile = $("#file").val(); var tempFilePath = $("#key").val(); var tempNewFile = tempFilePath + "/" + random_string() + get_suffix(tempFile); $("#key").val(tempNewFile); $('#form2').ajaxSubmit({ dataType: 'json', success: function () { $("#PicAddr").val("https://XXX/" + tempNewFile); }, error: function () { $.modalAlert("上传失败,请稍候重试", "error"); } }); } }); this.loadOssTokenInfo(); }
代码顺序有点凌乱,大概就是这样的思路,还遇到一个问题就是编辑器里的图片上传,我用的tinymce,在里面的upload插件里照住这个改一通,有个要注意的就是form里的id是需要设置不一样的,方便赋值与取值,但不影响表单提交的,去oss后台设置一些图片处理的方法,比如裁切、水印等,前台调用直接多加个后缀就ok了,大功告成,哦了。