手把手教你如何利用阿里云上传文件

    技术2022-07-11  59

    最近由于公司业务需要,我需要使用阿里云来保存视频文件。然后访问的话需要一个临时的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(); } }; }])

    好,到这里就介绍完了客户端直传的方式,如果有什么不对的地方请多包涵,请给我指正~谢谢

    Processed: 0.009, SQL: 9