持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情
前言
上一章了讲解了云存储的方法的后端上传,并且详细的分析了该方案存在的问题,本文将讲解前端如何直连OSS上传文件。
前端上传流程
- 用户向应用服务器申请Policy请求
- 应用服务器返回上传Policy和签名给用户。
- 用户发送上传请求到OSS
具体实现
前端发送Policy请求
$.ajax({
url: "/app/common/oss/getOssPolicyKey",
type: "post",
data: {
"ossPath" : ossPath
},
success: function(data) {
...
}
});
后端接口实现
@GetMapping("getOssPolicyKey")
public Map<String, String> getOssPolicyKey(HttpServletResponse resp,HttpServletRequest request)
{
// 获取上传目录
String ossPath = request.getParameter("ossPath");
logger.info("enter oss getOssPolicyKey, ossPath:{}", ossPath);
// 获取OSS授权
String endpoint = ossProperties.getEndpoint();
String accessId = ossProperties.getAccessKeyId();
String accessKey = ossProperties.getAccessKeySecret();
String bucket = ossProperties.getBucket();
//文件大小
String fileMaxSize = ossProperties.getFileMaxSize();
//过期时间
int expire = ossProperties.getExpire();
String host = "https://" + bucket + "." + endpoint;
OSSClient client = new OSSClient(endpoint, accessId, accessKey);
resp.setHeader("Access-Control-Allow-Origin", "*");
Map<String, String> respMap = new LinkedHashMap<String, String>();
try
{
long expireTime = expire * 60;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
//过期时间
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(
PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith,
PolicyConditions.COND_KEY, ossPath);
String postPolicy = client.generatePostPolicy(expiration,
policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = client.calculatePostSignature(postPolicy);
respMap.put("accessid", accessId);
respMap.put("accessKey", accessKey);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", ossPath);
respMap.put("host", host);
respMap.put("expire",
DateUtil.formatIso8601Date(expiration));
respMap.put("fileMaxSize", fileMaxSize);
logger.info("dir:" + ossPath + ",accessid:" + accessId
+ ", policy:" + encodedPolicy + ",signature:"
+ postSignature + ",host:" + host + ",expire:"
+ DateUtil.formatIso8601Date(expiration));
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "GET, POST");
logger.info("oss getOssPolicyKey end");
}
catch (Exception e)
{
logger.error("oss getOssPolicyKey error", e);
}
return respMap;
}
说明:需要设置签名的有效时间、上传文件的大小等参数。
返回结果:
{
"accessid": "myaccessid",
"accessKey": "myaccessKey",
"policy": "eyJleHBpcmF0aW9uIjoiMjAyMi0wNi0wN1QxNDozNToyNy4yMjlaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF0sWyJzdGFydHMtd2l0aCIsIiRrZXkiLCIvdGVzdCJdXX0=",
"signature": "hGzkGIvv0sSGlr864mozP1CvmLA=",
"dir": "/test",
"host": "myhost",
"expire": "2022-06-07T14:35:27.229Z",
"fileMaxSize": "5"
}
说明:通过调用后端接口返回相关的信息给前端,前端通过相关参数,调用oss的相关组件上传文件。
plupload上传OSS
前端上传组件采用的是plupload,其官网地址如下:
核心js代码
前端实现的方式有很多种,例如vue、js等,本文采用的是js方式实现
function initUpload(fileDiv)
{
var fileMaxSize = $("#"+fileDiv+"_fileMaxSize").val();
//上传按钮
var uploadButton = fileDiv + "UploadBtn";
//文件选择按钮
var selectFileButton = fileDiv + "SelectBtn";
var uploader = new plupload.Uploader({
runtimes : 'html5,flash,silverlight,html4',
browse_button : selectFileButton,
filters:{
max_file_size : fileMaxSize,
prevent_duplicates : false
},
multi_selection: true,
container: document.getElementById(fileDiv),
flash_swf_url : '${ctx}/js/plugins/oss/lib/plupload-2.1.2/js/Moxie.swf',
silverlight_xap_url : '${ctx}/js/plugins/oss/lib/plupload-2.1.2/js/Moxie.xap',
url : 'http://oss.aliyuncs.com',
init: {
PostInit: function() {
setInterval(getAccessKey(fileDiv), 9*60*1000);
$("#" + uploadButton).click(function(){
uploader.start();
return false;
});
},
FilesAdded: function(up, files) {
plupload.each(files, function(file) {
});
// 打开遮罩
$.util.showProgressLoading();
//初始化遮罩参数
initProgressText(files);
uploader.start();
},
BeforeUpload: function(up, file) {
setUploadParams(up, fileDiv, file);
//如果数据库当前目录下有相同的文件名 不允许添加
var flag = "false";
//TableOid
var tableOid = $("#" + fileDiv + "_tableOid").val();
//TableName
var tableName = $("#" + fileDiv + "_tableName").val();
var fileName = file.name;
var requestUrl = ctxPath+ "/app/sys/dataFile/validateFileNameRepeat";
var reqLoadFile = {
url : requestUrl,
dataType : 'json',
type : 'post',
async : false,
data : {
"tableName" : tableName,
"tableOid" : tableOid,
"fileName" : fileName
},
success : function(d) {
},
error : function(xhr){
alert(xhr);
}
};
$.util.ajax(reqLoadFile);
if("true" == flag){
return false;
}
up.start();
},
UploadProgress: function(up, file) {
//获取文件上传进度区域
var fileUploadDiv = document.getElementById("test_ProcessDiv");
//显示当前进度百分比
fileUploadDiv.getElementsByTagName('b')[0].innerHTML = '<span>' + file.percent + "%</span>";
var prog = fileUploadDiv.getElementsByTagName('div')[0];
var progBar = prog.getElementsByTagName('div')[0];
//更新进度条
progBar.style.width= file.percent+'%';
progBar.setAttribute('aria-valuenow', file.percent);
},
FileUploaded: function(up, file, info) {
if (info.status == 200)
{
addProgressText();
//调用后端接口,将已上传文件记录到DB中.
syncFileRecordToDB(up, file, fileDiv);
}
else
{
//上传失败,打印错误信息
alert("上传状态:" + info.status + "," + info.response);
//从待上传区域中删除文件
deletePreUploadFile(up, file);
}
},
UploadComplete : function(uploader,files){
//刷新资料类型列表
loadFileTypeList();
//刷新资料文件列表
loadFileList();
// 去除遮罩层
$.util.hideProgressLoading();
},
Error: function(up, err) {
//OSS返回错误信息
if(err.response)
{
alert($(err.response).find("Message").text());
//从待上传区域中删除文件
deletePreUploadFile(up, err.file);
return;
}
//错误信息
if(err.code)
{
switch(err.code)
{
case -100:
alert("上传文件错误");
break;
case -200:
alert("网络发生错误");
break;
case -300:
alert("磁盘读写错误");
break;
case -400:
alert("安全问题,上传错误");
break;
case -500:
alert("初始化错误");
break;
case -600:
alert("文件超过设定大小"+fileMaxSize);
break;
case -601:
alert("文件类型错误");
break;
case -602:
alert("文件重复");
break;
case -700:
alert("图片格式错误");
break;
case -701:
alert("内存错误");
break;
case -702:
alert("文件过大");
break;
default:
alert("文件上传错误");
}
}
}
}
});
uploader.init();
}
说明:相关使用可以参考官方文档。
扩展知识
OSS除了提供存储之外,还提供了多样化的图片处理,例如图片缩放、图片水印、图片压缩等操作,如果我们上传的文件图片比较大,我们可以在引用oss的图片时添加相关参数对图片进行处理,从而减少相关的流量。
设置图片宽度和高度等比例缩放
有时候有些图片不符合显示要求,我们可以对图片进行缩放处理。
https://xx/dish/globalDish/33_20220525163152.jpg?x-oss-process=image/resize,w_100/h_100
w_100,h_100:将图片的宽度和高度缩放到100.
图片添加水印
之前很多实现都是通过相关的工具类来对图片添加水印,其实OSS提供相关的功能实现非常简单。
- text:指定文字水印的文字内容,文字内容需进行Base64编码。
- type:指定文字水印的字体,字体名称需进行Base64编码。默认值:d3F5LXplbmhlaQ
- color:指定文字水印的文字颜色,参数值为RGB颜色值。
- size:指定文字水印的文字大小。
- shadow:指定文字水印的阴影透明度。
- rotate:指定文字顺时针旋转角度。
- t:指定图片水印或水印文字的透明度。
- g:指定水印在图片中的位置。
- x:坐标
- y:坐标
base64位文字编码
/**
* 水印编码: 将内容编码成Base64
*
* @param str
* @return
*/
private static String encode(String str){
byte[] encode = java.util.Base64.getEncoder().encode(str.getBytes());
try {
// 1.将结果中的加号(+)替换成短划线(-) 2.将结果中的正斜线(/)替换成下划线(_) 3.将结果中尾部的等号(=)省略
return new String(encode, "UTF-8").replace("+","-").replace("/","_");
} catch (UnsupportedEncodingException e) {
logger.error("encode error",e);
}
return str;
}
总结
本文介绍了阿里云文件存储采用的前端上传的方案,通过此方案能够提升相关性能和节省服务器的流量,在实际的项目中建议采用此方案。