最近由于公司业务需要,我需要使用阿里云来保存视频文件。然后访问的话需要一个临时的url可供使用,这样不用担心长时间暴露签名信息和地址。但是完全没做过这个东西,心里是慌得一笔。手动滑稽~~~。但是也是没办法,硬着头皮冲啦~
不过还好阿里云的社区有最佳实践的例子,可以参考下~
下面废话不多说进入正题:
一、文件设置临时访问路径
1.引入OSS依赖
直接从maven仓库去找oss的依赖就行,然后版本的话随意,个人推荐选用的人比较多版本,也不要选beta版本的,这样遇到坑的几率小一点,不要问我为什么这么说,都是泪,哈哈。实在不想选下面也给准备好了,ctrl+v就行。
<!-- oss依赖 -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.8.0</version>
</dependency>
2.在阿里云平台创建子用户
本文的前提是你已经创建好了bucket,并且已经有文件在内,需要实现的只是生成临时访问的url。
一定一定要小心权限问题,子用户给够用的权限就行,RAM角色也是。
- 进入阿里云的控制台找到右上角的控制台,找到下方的对象存储 oss。
- 进入bucket列表就可以看到自己的桶列表,
- 点击权限管理 > 访问控制RAM,就可以创建自己的角色和RAM角色,创建用户的时候选择编程访问,这样就可以启用 AccessKey ID 和 AccessKey Secret,支持通过 API 或其他开发工具访问。创建完成后就可以给角色授权。给刚好能用的权限即可,不要给太过了,特别是给公司写功能的小伙伴一定要记住这个。
- 那么要想临时访问的oss就需要一个临时的凭证信息,这里使用了OSS SDK 和 STS SDK结合生成凭证信息。原理图给到下方了,我们需要的角色权限是AliyunSTSAssumeRoleAccess权限,使得我们可以调用STS接口生成安全令牌(SecurityToken)、临时访问密钥(AccessKeyId和AccessKeySecret)以及过期时间。以上几样就是一个完整的凭证信息,那我们可以通过这些生成一个临时的URL对象。
- 接下来要去创建权限策略,在左侧导航栏的权限管理菜单下,单击权限策略管理,再点击单击新建权限策略就可以创建了。配置模式选择可视化配置或脚本配置。以脚本配置为例,对ram-test添加ListObjects与GetObject等只读权限,在策略内容中配置脚本示例如下:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:ListObjects",
"oss:GetObject"
],
"Resource": [
"acs:oss:*:*:ram-test",
"acs:oss:*:*:ram-test/*"
]
}
]
}
需要配置更细颗粒度的权限,点击这里查看。
- 创建Ram角色并记录角色ARN,还是RAM控制台下方,
点击创建RAN角色,我使用的是阿里云账号创建的,创建完成后,我们需要给角色配置权限,给角色配置下上面添加的权限策略,还有AliyunOSSReadOnlyAccess权限,因为最后是要访问oss文件的,所以需要这个权限。顺便把角色信息的ARN记录一下,生成临时URL需要这个信息。
3.本地代码实现生成凭证信息
下面给到阿里云官方的示例代码:
package com.yijiupi.himalaya.boaomath.basic.domain.bl.aliyun;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.yijiupi.himalaya.base.utils.AssertUtils;
import com.yijiupi.himalaya.boaomath.basic.domain.http.properties.AliyunPolicyProperties;
import com.yijiupi.himalaya.boaomath.basic.domain.http.properties.StsAccessProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.net.URL;
import java.util.Date;
/**
* @author LiJunJie
* @Descripiton:Sts获取临时访
* @create 2020-07-01 9:54
*/
@Service
public class StsAccessBL {
@Autowired
private StsAccessProperties stsAccessProperties;
@Autowired
private AliyunPolicyProperties aliyunPolicyProperties;
private static final Logger LOG = LoggerFactory.getLogger(StsAccessBL.class);
/**
* 获取临时访问凭证以及临时访问RAM角色的密钥信息
* @return
*/
public String getAccessSecurityToken() {
String endpoint = stsAccessProperties.getEndpoint();
String AccessKeyId = stsAccessProperties.getAccessKeyId();
String accessKeySecret = stsAccessProperties.getAccessKeySecret();
String roleArn = stsAccessProperties.getRoleArn();
String roleSessionName = stsAccessProperties.getRoleSessionName();
String policy = "{\n" +
" \"Version\": \"1\", \n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": [\n" +
" \"oss:*\"\n" +
" ], \n" +
" \"Resource\": [\n" +
" \"acs:oss:*:*:*\" \n" +
" ], \n" +
" \"Effect\": \"Allow\"\n" +
" }\n" +
" ]\n" +
"}";
try {
// 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
DefaultProfile.addEndpoint("", "", "Sts", endpoint);
// 构造default profile(参数留空,无需添加region ID)
IClientProfile profile = DefaultProfile.getProfile("", AccessKeyId, accessKeySecret);
// 用profile构造client
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setPolicy(policy); // 若policy为空,则用户将获得该角色下所有权限
request.setDurationSeconds(901L); // 设置凭证有效时间
final AssumeRoleResponse response = client.getAcsResponse(request);
String securityToken = response.getCredentials().getSecurityToken();
String keyId = response.getCredentials().getAccessKeyId();
String keySecret = response.getCredentials().getAccessKeySecret();
return securityToken + "]" + keyId + "]" + keySecret;
} catch (ClientException e) {
LOG.info("GET TOKEN Failed:RequestId " + e.getRequestId());
} catch (ServerException e) {
e.printStackTrace();
} catch (com.aliyuncs.exceptions.ClientException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据token信息临时访问url
* @param fileName
* @return
*/
public String accessVideoByToken(String fileName) {
if (!StringUtils.hasText(fileName)) {
AssertUtils.fail("文件名不能为空");
}
String accessSecurityTokenInfo = getAccessSecurityToken();
String[] tokenInfo = accessSecurityTokenInfo.split("]");
if(tokenInfo.length <= 0 || tokenInfo.length > 3){
AssertUtils.fail("token信息获取失败");
}
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = aliyunPolicyProperties.getEndpoint();
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
String AccessKeyId = tokenInfo[1];
String accessKeySecret =tokenInfo[2];
String securityToken = tokenInfo[0];
String bucketName = aliyunPolicyProperties.getBucketName();
String objectName = aliyunPolicyProperties.getFileHost() + fileName;
// 用户拿到STS临时凭证后,通过其中的安全令牌(SecurityToken)和临时访问密钥(AccessKeyId和AccessKeySecret)生成OSSClient。
// 创建OSSClient实例。注意,这里使用到了上一步生成的临时访问凭证(STS访问凭证)。
OSSClient ossClient = new OSSClient(endpoint, AccessKeyId, accessKeySecret, securityToken);
// OSS操作。
Date expiration = new Date(new Date().getTime() + 300 * 1000);
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucketName, objectName, expiration);
// 关闭OSSClient。
ossClient.shutdown();
return String.valueOf(url);
}
}
上面这种方式很安全,但是有一个缺点是限定的时间最长为12小时,我向阿里工作人员提交工单求助过,无果,做多只能12小时。想要时间长一点可以使用下面这种:
public String accessVideoByToken(String fileName, Date expirationTime) {
if (!StringUtils.hasText(fileName)) {
AssertUtils.fail("文件名不能为空");
}
String endpoint = aliyunPolicyProperties.getEndpoint();
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
String accessKeyId = stsAccessProperties.getAccessKeyId();
String accessKeySecret = stsAccessProperties.getAccessKeySecret();
String bucketName = aliyunPolicyProperties.getOutputBucket();
String fileHost = aliyunPolicyProperties.getFileHost();
if (!Objects.equals(env, "production")) {
fileHost = "test/";
}
String objectName = fileHost + fileName;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
// OSS操作。
// 生成以GET方法访问的签名URL,访客可以直接通过浏览器访问相关内容。
URL url = ossClient.generatePresignedUrl(bucketName, objectName, expirationTime);
// 关闭OSSClient。
ossClient.shutdown();
return String.valueOf(url);
}
密钥信息存放
密钥信息放在代码里肯定是比较难维护的,那么大佬们肯定已经想到了,放在application的配置文件不就好了。所以我们需要在配置文件中放入密钥信息。当然你可以存入数据库,放出修改配置信息的的接口。下面介绍下使用properties文件的方式。
properties文件中:
# OSS 密钥信息
oss.file.accessKeyId=***
oss.file.accessKeySecret=***
oss.file.roleArn=***
oss.file.roleSessionName=***
oss.file.policy=***
那么提取这些信息我们需要用到@ConfigurationProperties注解,这里就不详细解释了,百度下就行。这里介绍下使用spring自带的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
@Component
//接收application.yml中的oss.file前缀的属性
@ConfigurationProperties(prefix = "oss.file")
public class OssProperties {
private String accessKeyId;
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
省略···
}
那么可以顺利读取之后,只需要在需要使用的地方依赖注入就行,使用@Autowired注解就行,如下。
@Autowired
private OssProperties ossProperties;
相关参数代码中注解都有介绍这里就不详细介绍了,可以看到生成的安全令牌等信息都在AssumeRoleResponse对象里可以获取到,获取到这些之后就可以通过这些信息生成临时的url进行访问。
至此呢,临时访问路径就生成好了,将路径返回给客户端就行,可以根据需要去修改下部分逻辑。
二、文件上传
文件上传大致可分为后端上传和前端直传的方式,后端上传的方式较为简单,获取表单上传对象的文件流即可,前端直传的方式因为我个人对前端不精,做的比较费力。
后端上传
这里以springboot工程为例,控制器获取文件流,使用ossClient.putObject上传数据流到OSS。
@PostMapping("templates/oss/upload")
public BaseResult upload(MultipartFile file) {
try {
InputStream is = file.getInputStream();
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
String accessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
//bucketName是你的ossBucket的名称,fileName是需要存储文件的名称
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, is);
// 如果需要上传时设置存储类型与访问权限,请参考以下示例代码。
ObjectMetadata metadata = new ObjectMetadata();
metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
metadata.setObjectAcl(CannedAccessControlList.Private);
putObjectRequest.setMetadata(metadata);
ossClient.putObject(putObjectRequest);
// 关闭OSSClient。
ossClient.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
return BaseResult.getSuccessResult();
}
或者你想上传某处的字节类型数组:
// Endpoint以杭州为例,其它Region请按实际情况填写。
String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
String accessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
// 上传Byte数组。(这里就是要上传的数组)
byte[] content = "Hello OSS".getBytes();
ossClient.putObject("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content));
// 关闭OSSClient。
ossClient.shutdown();
此外阿里的还支持断点续传等方式,详情可以访问阿里的最佳实践,点击跳转。
总之后端上传的方式很简单,下面讲一下前端上传的方式,我是直接去下载了阿里的实列包。
客户端获取签名直传
下载阿里的实列包。aliyun-oss-appserver-js-master.zip
下载好上面的压缩包。引入plupload.js
可自行选择需要的文件,实列的演示在包内的index.html文件内,基础好一点的打开一看可能就会了,而我这种小辣鸡就一脸懵逼呀~
初次之外你还需要js去使用,放出我的(angular项目的):
(function () {
'use strict';
var pluploadModule = angular.module("pluploadModule", []);
pluploadModule.directive('plupload', ['$timeout', 'ejpAlert', function ($timeout, ejpAlert) {
return {
restrict: 'A', // E = Element, A = Attribute, C = Class, M = Comment
scope: {
selectFileFun: "=",//选择文件
fileUploaderSuccessFun: "=",//上传成功之后
uploadProgress: "=",//触发进度条
getUploadFileName: "=",//获取随机生成的文件名
clickUploadBtn: "=",//点击开始上传按钮
maxFileSize: '@',
uploadFileType: '=',
categoryType: '='
},
link: function ($scope, iElm, iAttrs, controller) {
let accessid = '',
vaaccesskey = '',
host = '',
policyBase64 = '',
signature = '',
callbackbody = '',
filename = '',
key = '',
expire = 0,
g_object_name = '',
g_object_name_type = '';
let now = Date.parse(new Date()) / 1000;
function send_request(fileType) {
var xmlhttp = null;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
let category = $scope.categoryType;
if (xmlhttp != null) {
// serverUrl是 用户获取 '签名和Policy' 等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
let serverUrl = null;
if (fileType > -1) {
serverUrl = 'oss/getPublicPolicy?fileType=' + fileType+"&category="+category;
} else {
serverUrl = 'oss/getPolicy';
}
xmlhttp.open("GET", serverUrl, false);
xmlhttp.send(null);
return xmlhttp.responseText
} else {
alert("Your browser does not support XMLHTTP.");
}
};
function check_object_radio() {
g_object_name_type = 'random_name';
}
function get_signature(fileType) {
// 可以判断当前expire是否超过了当前时间, 如果超过了当前时间, 就重新取一下,3s 作为缓冲。
now = Date.parse(new Date()) / 1000;
if (expire < now + 3) {
let body = send_request(fileType)
var obj = eval("(" + body + ")").data;
host = obj['host']
policyBase64 = obj['policy']
accessid = obj['accessid']
signature = obj['signature']
expire = parseInt(obj['expire'])
callbackbody = obj['callback']
key = obj['dir']
return true;
}
return false;
};
function random_string(len) {
len = len || 32;
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPos = chars.length;
var pwd = '';
for (i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
function getNowTimeFun() {
let myDate = new Date();
let year = myDate.getFullYear();
let month = timeCoverFun(myDate.getMonth() + 1);
let date = timeCoverFun(myDate.getDate());
let hours = timeCoverFun(myDate.getHours());
let minutes = timeCoverFun(myDate.getMinutes());
let seconds = timeCoverFun(myDate.getSeconds());
let milliseconds = timeCoverFun(myDate.getMilliseconds());
return (year + month + date + hours + minutes + seconds + milliseconds);
}
function timeCoverFun(timeText) {
if (timeText < 10) {
timeText = "0" + timeText;
}
return timeText;
}
function get_suffix(filename) {
let pos = filename.lastIndexOf('.')
let suffix = ''
if (pos != -1) {
suffix = filename.substring(pos)
}
return suffix;
}
function calculate_object_name(filename) {
if (g_object_name_type == 'local_name') {
g_object_name += `${filename}`;
} else if (g_object_name_type == 'random_name') {
let suffix = get_suffix(filename)
g_object_name = getNowTimeFun() + suffix;
}
let fileType = $scope.uploadFileType;
if (fileType == -1) {
$scope.getUploadFileName(g_object_name);
} else if (fileType == 0 || fileType == 1 || fileType == 2) {
$scope.getUploadFileName(host + "/" + key + g_object_name);
}
return g_object_name;
}
function set_upload_param(up, filename, ret) {
let fileType = $scope.uploadFileType;
if (ret == false) {
ret = get_signature(fileType)
}
g_object_name = key;
if (filename != '') {
g_object_name = encodeURIComponent(calculate_object_name(filename))
}
let new_multipart_params = {
'key': key + g_object_name,
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status': '200', //让服务端返回200,不然,默认会返回204
// 'callback' : callbackbody,
'signature': signature,
};
up.setOption({
'url': host,
'multipart_params': new_multipart_params
});
up.start();
}
var uploader = new plupload.Uploader({
runtimes: 'html5,flash,silverlight,html4',
browse_button: 'selectfiles',
// multi_selection: false,
container: document.getElementById('containerOSS'),
flash_swf_url: '/static/assets/aliyun-oss-appserver-js-master/lib/plupload-2.1.2/js/Moxie.swf',
silverlight_xap_url: '/static/assets/aliyun-oss-appserver-js-master/lib/plupload-2.1.2/js/Moxie.xap',
url: 'http://oss.aliyuncs.com',
filters: {
mime_types: [ //只允许上传视频和图片以及文档文件
{title: "Video files", extensions: "mp4,flv,mp3"},
{title: "Image files", extensions: "jpg,gif,png,bmp"},
{title: "Other files", extensions: "docx,xlsx,pdf,doc,xls"}
],
max_file_size: $scope.maxFileSize || '3000mb', //最大上传3000mb的文件
prevent_duplicates: false //不允许选取重复文件
},
init: {
PostInit: function () {
document.getElementById('postfiles').onclick = function () {
$scope.clickUploadBtn();
set_upload_param(uploader, '', false);
return false;
};
},
FilesAdded: function (up, files) {
$scope.selectFileFun(files[0]);
let fileType = $scope.uploadFileType;
let suffix = files[0].type.substring(files[0].type.lastIndexOf("/") + 1);
if (fileType == 0 || fileType == -1) {
if (suffix != "mp4" && suffix != "flv" && suffix != "mp3") {
ejpAlert.show("请选择视频文件~ (类型:mp4/flv/mp3)");
return;
}
} else if (fileType == 1) {
if (suffix != "jpg" && suffix != "jpeg" && suffix != "gif" && suffix != "png" && suffix != "bmp") {
ejpAlert.show("请选择图片文件(类型:jpg/gif/png/bmp)");
return;
}
}
},
BeforeUpload: function (up, file) {
check_object_radio();
set_upload_param(up, file.name, true);
},
UploadProgress: function (up, file) {
$scope.uploadProgress();
},
FileUploaded: function (up, file, info) {
if (info.status == 200) {
$scope.fileUploaderSuccessFun();
} else if (info.status == 203) {
ejpAlert.show("上传到OSS成功,但是oss访问用户设置的上传回调服务器失败,失败原因是:" + info.response);
} else {
ejpAlert.show(info.response);
}
},
Error: function (up, err) {
if (err.code == -600) {
ejpAlert.show("您选择的文件太大了~>-<");
} else if (err.code == -601) {
ejpAlert.show("您选择的文件格式不对!");
} else if (err.code == -602) {
ejpAlert.show("这个文件已经上传过一遍了哦!");
} else {
ejpAlert.show(err.response);
}
}
}
});
uploader.init();
}
};
}]);
}())
angular可以双向绑定,如果你的项目不是angular也没关系,实列包的upload.js是普通项目的示例,可以拷过去修改下。
使用xmlhttp请求签名,获取oss授权过的签名(由bucket的Ram角色密钥获取),
签名接口(我这里是直接将签名信息放到了map里):
/**
* 获取签名信息
*
* @param fileType 0 - video、 1 - image
* @return
*/
public Map<String, String> getPublicPolicy(Integer fileType) {
String accessId = aliyunPolicyProperties.getKeyId(); // 请填写您的AccessKeyId。
String accessKey = aliyunPolicyProperties.getKeySecret();// 请填写您的AccessKeySecret。
String endpoint = aliyunPolicyProperties.getEndpoint();// 请填写您的 endpoint。
String bucket = null;// 请填写您的 bucketname 。
String host = null; // host的格式为 bucketname.endpoint
String dir = null; // 用户上传文件时指定的前缀。
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey);
try {
long expireTime = 3600;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
if (Objects.equals(fileType, 0)) {
bucket = aliyunPolicyProperties.getPublicBucketName();
host = "https://" + bucket + "." + endpoint.substring(8);
dir = aliyunPolicyProperties.getFileHost();
// PostObject请求最大可支持的文件大小为1GB
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000L);
} else {
bucket = aliyunPolicyProperties.getPublicBucketName();
host = "https://" + bucket + "." + endpoint.substring(8);
dir = aliyunPolicyProperties.getImgFileHost();
// PostObject请求最大可支持的文件大小为10M
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 10485760L);
}
if (!Objects.equals(env, "production")) {
dir = "test/";
}
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes("utf-8");
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
Map<String, String> respMap = new LinkedHashMap<String, String>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
// respMap.put("expire", formatISO8601Date(expiration));
return respMap;
} catch (Exception e) {
AssertUtils.fail(e.getMessage());
} finally {
ossClient.shutdown();
}
return null;
}
修改计算文件名或者路径,作者这里有特殊需求就修改了:
文件配置:
添加文件的限制:
表单部分(angular):
<form name="form"
class="form-horizontal ng-valid ng-dirty ng-valid-parse">
<div class="form-body">
<div class="form-group col-md-11">
<label class="col-md-4 control-label">课程视频: <span class="required">*</span></label>
<!-- 选择图片 -->
<div class="col-md-8 promotion-img" id="containerOSS" plupload select-file-fun="selectFile" file-uploader-success-fun="uploadSuccessFun" upload-progress="uploadProgressFun" get-upload-file-name="getUploadFileNameFun" click-upload-btn="clickUploadBtnFun" max-file-size="3000mb" upload-file-type="theFileType" >
<span id="selectfiles" href="javascript:void(0);" class='btn btn-primary'>选择视频</span>
<span style="color: red;">注:文件大小3000M以下</span>
<br>
<div class="form-control-static" id="fileName"><span ng-bind="fileData.name" ></span> <b id="percent"></b></div>
<div class="progress" style="width: 200px;" ng-if="fileData.name">
<div class="progress-bar progress-bar-success" style="width: 0%;background: #5cb85c" id="progress-bar"></div>
</div>
</div>
</div>
<div class="form-group col-md-11">
<label class="col-md-4 control-label">课程讲师: <span class="required">*</span></label>
<p class="col-md-4 form-control-static"
ng-bind="dto.teacherName"></p>
<input type="text" name="teacherName" class="form-control"
ng-model="dto.teacherName" required style="display: none">
<p class="help-block" ng-show="form.teacherName.$touched"
ng-messages="form.teacherName.$error" ng-messages-multiple>
<span class="error-messages ng-scope ng-active"
ng-message="required">要选择讲师!</span>
</p>
<button class="btn bg-blue" ng-click="selectTeacher()" type="button"><i
class="fa fa-link"></i> 选择
</button>
</div>
<div class="form-group col-md-11">
<label class="col-md-4 control-label">视频描述: <span class="required">*</span></label>
<div class="col-md-6">
<input type="text" name="videoDesc" class="form-control"
ng-model="dto.videoDesc" required >
<p class="help-block" ng-show="form.videoDesc.$touched"
ng-messages="form.courseDesc.$error" ng-messages-multiple>
<span class="error-messages ng-scope ng-active"
ng-message="required">描述不能为空!</span>
</p>
</div>
</div>
</div>
<div class="form-group ">
<div class="col-md-6 col-md-offset-4">
<button type="button" class="btn btn-primary" id="postfiles" ng-disabled="hasClickUploadBtn">
<i class="fa fa fa-arrow-up"></i> 开始上传
</button>
<button type="button" class="btn btn-default" ng-click="cancel()">
<i class="fa fa fa-close"></i> 取消
</button>
</div>
</div>
</form>
页面js:
// 上传视频
MetronicApp.controller("addVideoController", [
'$rootScope',
'$scope',
'$state',
'settings',
'$http',
'$location',
'$modal',
'getUserInfo',
'pagedataLoading',
'QueryDataService',
'ejpAlert',
'data',
'$modalInstance',
function ($rootScope, $scope, $state, settings, $http,
$location, $modal, getUserInfo, pagedataLoading,
QueryDataService, ejpAlert, data, $modalInstance) {
$scope.dto = {};
$scope.dto.courseProgressId = data.item.courseProgressInfoId;
$scope.dto.videoUrl = null;
$scope.title = "上传视频";
$scope.isSuccessUpload = false;
$scope.theFileType = null;
$scope.hasClickUploadBtn = true;
/**
* 选择文件
* @param fileData
*/
$scope.selectFile = function (fileData) {
$scope.hasClickUploadBtn = false;
$scope.fileData = fileData;
//文件类型
$scope.theFileType = -1;
$scope.$apply();
}
/**
* 获取随机生成文件名
* @param fileName
*/
$scope.getUploadFileNameFun = function (fileName) {
$scope.dto.videoFileName = fileName;
}
/**
* 开始上传
*/
$scope.clickUploadBtnFun = function () {
$scope.hasClickUploadBtn = true;
}
/**
* 上传视频之后调用
*/
$scope.uploadSuccessFun = function () {
$http.post(" ", $scope.dto)
.success(function (data) {
if (data.result === 'success') {
ejpAlert.show("已上传!");
//重置参数
$scope.hasClickUploadBtn = false;
$scope.isSuccessUpload = true;
$modalInstance.close();
}
})
}
/**
* 进度条显示
*/
$scope.uploadProgressFun = function () {
let fileData = $scope.fileData;
let progBar = document.getElementById('progress-bar');
let percent = document.getElementById('percent');
if (progBar && percent) {
percent.innerHTML = '<span>' + fileData.percent + "%</span>";
progBar.style.width = 2 * fileData.percent + 'px';
progBar.setAttribute('aria-valuenow', fileData.percent);
}
}
/**
* 取消
*/
$scope.cancel = function () {
if (!$scope.isSuccessUpload && $scope.hasClickUploadBtn) {
ejpAlert.confirm("正在上传视频,取消后将会看不到视频上传进度,确认取消吗?").result.then(function () {
let percent = document.getElementById('percent');
percent.innerHTML = '';
let progBar = document.getElementById('progress-bar');
if (progBar != null && typeof(progBar) != "undefined"){
progBar.setAttribute('aria-valuenow', "");
}
$modalInstance.close();
})
} else {
$modalInstance.close();
}
};
}])
好,到这里就介绍完了客户端直传的方式,如果有什么不对的地方请多包涵,请给我指正~谢谢