• 前端直传阿里云OSS


    阿里云对象存储服务(Object Storage Service,简称OSS),是阿里云对外提供的海量、安全、低成本、高可靠的云存储服务。

    目前通过Web端直传文件(Object)到OSS,有两种方案:

    一、利用OSS Browser.js SDK将文件上传到OSS。该方案通过OSS Browser.js SDK直传数据到OSS,支持断点续传,支持各种主流浏览器,可以将File对象、Blob数据以及OSS Buffer上传OSS,该方案还支持下载和删除

    二、利用OSS提供的PostObject接口来实现表单上传,不支持断点续传,支持h5,小程序,支持uniapp的uni.uploadFile接口

    方案一:使用阿里云SDK上传

    由于前端环境不安全,为避免暴露阿里云账号访问密钥(AccessKey ID和AccessKey Secret),该方案需要搭建STS服务获取临时访问密钥(AccessKey ID和AccessKey Secret)和安全令牌(SecurityToken),需要先开通STS服务,参考官方文档

    后端

    后端需要导入aliyun-sdk-oss包,用于获取前端需要的key和secret

    JDK版本:jdk8

    <dependency>
        <groupId>com.aliyun.ossgroupId>
        <artifactId>aliyun-sdk-ossartifactId>
        <version>2.0.7version>
        
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果是java9及以上版本,则需要添加jaxb相关依赖。添加jaxb相关依赖示例代码如下:

    <dependency>
        <groupId>javax.xml.bindgroupId>
        <artifactId>jaxb-apiartifactId>
        <version>2.3.1version>
    dependency>
    <dependency>
        <groupId>javax.activationgroupId>
        <artifactId>activationartifactId>
        <version>1.1.1version>
    dependency>
    
    <dependency>
        <groupId>org.glassfish.jaxbgroupId>
        <artifactId>jaxb-runtimeartifactId>
        <version>2.3.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    搭建STS服务(部分代码)

    	// STS接入地址,例如sts.cn-hangzhou.aliyuncs.com。
      	@Value("${ramEndpoint}")
        private String ramEndpoint;
        // 访问密钥AccessKey ID和AccessKey Secret
        @Value("${ramAccessKeyId}")
        private String ramAccessKeyId;
        @Value("${ramAccessKeySecret}")
        private String ramAccessKeySecret;
        // 角色ARN
        @Value("${ramRoleArn}")
        // 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest
        private String ramRoleArn;
        @Value("${ramRoleSessionName}")
        private String ramRoleSessionName;
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        RedisTemplate redisTemplate;
    
        /**
         * 通过RAM子账号获取stsToken,作为临时凭据
         */
        @RequestMapping(value = "getStsToken", method = {RequestMethod.GET, RequestMethod.POST})
        public Object getStsToken() {
            ResponseVo responseVo = new ResponseVo();
            AssumeRoleResponse response = null;
            Object redisToken = null;
            try {
                redisToken = redisTemplate.opsForValue().get("stsToken");
            } catch (Exception e) {
                logger.error(e.getMessage());
                responseVo.setError(GlobalErrorCode.SYS_RUN_ERROR.getCode());
                return responseVo;
            }
            if (redisToken != null) {
                response = JSONObject.parseObject(redisToken.toString(), AssumeRoleResponse.class);
                responseVo.setSuccess(response);
                return responseVo;
            } else {
                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 {
                    DefaultProfile.addEndpoint("", "", "Sts", ramEndpoint);
                    // 构造default profile(参数留空,无需添加region ID)
                    IClientProfile profile = DefaultProfile.getProfile("", ramAccessKeyId, ramAccessKeySecret);
                    // 用profile构造client
                    DefaultAcsClient client = new DefaultAcsClient(profile);
                    final AssumeRoleRequest request = new AssumeRoleRequest();
                    request.setMethod(MethodType.POST);
                    request.setRoleArn(ramRoleArn);
                    request.setRoleSessionName(ramRoleSessionName);
                    request.setPolicy(policy); // 若policy为空,则用户将获得该角色下所有权限
                    request.setDurationSeconds(20 * 60L); // 设置凭证有效时间,单位秒
                    //获取凭证
                    response = client.getAcsResponse(request);
                    /*
                     * 缓存该凭证,凭证失效后才从OSS再次获取凭证
                     * 凭证有效时间为20分钟,Redis里只缓存10分钟
                     */
                    redisTemplate.opsForValue().set("stsToken", JSONObject.toJSONString(response), 10 * 60, TimeUnit.SECONDS);
                    responseVo.setSuccess(response);
                    return responseVo;
                } catch (ClientException e) {
                    logger.error(e.getErrMsg());
                    responseVo.setError(GlobalErrorCode.SYS_RUN_ERROR.getCode());
                    return responseVo;
                }
            }
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    参考阿里云文档

    前端

    安装

    $ npm install ali-oss --save
    
    • 1

    部分代码

    onLoad() {
    	this.getStsToken()
    },
    
    • 1
    • 2
    • 3

    methods

    /**
    * @param {String} pathAndName Object完整路径。Object完整路径中不能包含Bucket名称("exampledir/exampleobject.txt")
    * @param {Object} data (file对象、Blob数据或者OSS Buffer)
    */
    async putObject(pathAndName, data) {
        try {
            // 您可以通过自定义文件名(例如exampleobject.txt)或文件完整路径(例如exampledir/exampleobject.txt)的形式实现将数据上传到当前Bucket或Bucket中的指定目录。
            const result = await this.getClient().put(
                pathAndName,
                data
            );
            console.log('result:', result);
        } catch (e) {
            console.log(e);
        }
    },
        getClient() {
            if (this.client) {
                return this.client
            }
            const OSS = require('ali-oss');
    
            const client = new OSS({
                // yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
                region: 'oss-cn-qingdao',
                // 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
                accessKeyId: this.stsToken.credentials.accessKeyId,
                accessKeySecret: this.stsToken.credentials.accessKeySecret,
                // 从STS服务获取的安全令牌(SecurityToken)。
                stsToken: this.stsToken.credentials.securityToken,
                refreshSTSToken: async () => {
                    // 向您搭建的STS服务获取临时访问凭证。
                    let info = await this.$post(GET_STS_TOKEN)
                    info = info.data
                    console.log('-----------refresh--token')
    
                    return {
                        accessKeyId: info.credentials.accessKeyId,
                        accessKeySecret: info.credentials.accessKeySecret,
                        stsToken: info.credentials.securityToken
                    }
                },
                // 刷新临时访问凭证的时间间隔,单位为毫秒。每隔一段时间定时器会自动掉后台接口刷新token
                refreshSTSTokenInterval: 600000,
                // 填写Bucket名称。
                bucket: 'zxxxxth-bucket'
            });
    
            this.client = client
            return this.client
        },
            getStsToken() {
                //从后台获取stsToken(改成自己的前端请求接口)
                this.$post(GET_STS_TOKEN).then(rsp => {
                    if (rsp.success) {
                        this.stsToken = rsp.data;
                        // 初始化一下client让定时任务启动,自动刷新token(避免过期)
                        this.getClient()
                        console.log('this.stsToken:', this.stsToken)
                    } else {
                        uni.showToast({
                            title: rsp.message,
                            duration: 2000
                        });
                    }
                })
            },              
                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    refreshSTSToken参数说明:当初始化new OSS()时,定时器会启动,当时间到了refreshSTSTokenInterval所设置的值时,并不会立即调用后台接口获取token,只有手动触发put()接口时,才会调用后台接口获取token

    参考阿里云文档

    开通STS服务步骤

    方案二:使用PostObject接口来实现表单上传

    这个方案支持小程序上传,uniapp上传。无需开通STS服务

    后端

    获取postObject接口需要的policy,OSSAccessKeyId,signature 参考官方文档

    这里签名使用后端签名,所以不需要申请开通STS服务

    简化版,无回调
    /**
         * 利用OSS提供的PostObject接口,通过表单上传的方式将文件上传到OSS。
         * 该方案兼容大部分浏览器,但在网络状况不好的时候,如果单个文件上传失败,
         * 只能重试上传。上传的Object大小不能超过5 GB。
         * @return ResponseVo{success:true,message:'',data:{},code:200}
         */
        @RequestMapping(value = "getPostObjectParams", method = {RequestMethod.GET, RequestMethod.POST})
        public Object getPostObjectParams() {
            ResponseVo responseVo = new ResponseVo();
            responseVo.setSuccess(OSSServer.getPostObjectParams());
            return responseVo;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    OSSServer.class

    	public static OSSClient getOSSClient() {
    		if (null == ossClient) {
    			ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret);
    		}
    		return ossClient;
    	}	
    
    		/**
    	 * 获取表单上传的方式的PostObject参数
    	 * @return
    	 */
    	public static Map<String, Object> getPostObjectParams() {
    		Map<String, Object> respMap = new LinkedHashMap();
    		// 限制参数的生效时间,单位为分钟,默认值为20。
    		int expireTime = 20;
    		// 限制上传文件的大小,单位为MB,默认值为100。
    		int maxSize = 100;
    		// 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
    		// 如果值为"test"那么前端的key参数必须以"test"开头,如test/*、test1.jpg、test/comment/11.jpg
    		String dir = "";
    
    		// 创建OSSClient实例。
    		OSS ossClient = getOSSClient();
    		try {
    			long expireEndTime = System.currentTimeMillis() + expireTime * 1000 * 60;
    			Date expiration = new Date(expireEndTime);
    			PolicyConditions policyConds = new PolicyConditions();
    			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize * 1024 * 1024);
    			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);
    
    
    			respMap.put("accessKeyId", accessKeyId);
    			respMap.put("policy", encodedPolicy);
    			respMap.put("signature", postSignature);
    
    			respMap.put("expire", expireEndTime / 1000);
    		} catch (Exception e) {
    			log.error("getPostObjectParams", e);
    		}
    
    		return respMap;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    大多数情况下,用户上传文件后,应用服务器需要知道用户上传了哪些文件以及文件名;如果上传了图片,还需要知道图片的大小等,为此OSS提供了上传回调方案。

    流程图:

    在这里插入图片描述

    当用户要上传一个文件到OSS,而且希望将上传的结果返回给应用服务器时,需要设置一个回调函数,将请求告知应用服务器。用户上传完文件后,不会直接得到返回结果,而是先通知应用服务器,再把结果转达给用户。

    有回调的PostObject参数
    /**
    	 * 获取表单上传的方式的PostObject参数【有回调】
    	 * @return
    	 */
    	public static Map<String, Object> getPostObjectParams() {
    		Map<String, Object> respMap = new LinkedHashMap();
    		// 限制参数的生效时间,单位为分钟,默认值为20。
    		int expireTime = 20;
    		// 限制上传文件的大小,单位为MB,默认值为10。
    		int maxSize = 10;
    		// 设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
    		// 如果值为"test"那么前端的key参数必须以"test"开头,如test/*、test1.jpg、test/comment/11.jpg
    		// 可以让用户没有办法上传到其他的目录,从而保证了数据的安全性
    		String dir = "";
    		// 设置上传回调URL,即回调服务器地址,用于处理应用服务器与OSS之间的通信。OSS会在文件上传完成后,把文件上传信息通过此回调URL发送给应用服务器。
    		String callbackUrl = "https://jmt.xxx.cn/common/postObjectCallBack/";
    
    		// 创建OSSClient实例。
    		OSS ossClient = getOSSClient();
    		try {
    			long expireEndTime = System.currentTimeMillis() + expireTime * 1000 * 60;
    			Date expiration = new Date(expireEndTime);
    			PolicyConditions policyConds = new PolicyConditions();
    			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize * 1024 * 1024);
    			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);
    
    
    			respMap.put("accessKeyId", accessKeyId);
    			respMap.put("policy", encodedPolicy);
    			respMap.put("signature", postSignature);
    			respMap.put("expire", expireEndTime / 1000);
    			// 配置回调地址
    			JSONObject jasonCallback = new JSONObject();
    			jasonCallback.put("callbackUrl", callbackUrl);
    //			jasonCallback.put("callbackBody",
    //					"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}");
    
    			jasonCallback.put("callbackBody", "{\"filename\":${object},\"mimeType\":${mimeType}}");
    
    			jasonCallback.put("callbackBodyType", "application/json");//application/x-www-form-urlencoded
    			String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
    			respMap.put("callback", base64CallbackBody);
    
    		} catch (Exception e) {
    			log.error("getPostObjectParams", e);
    		}
    
    		return respMap;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    给oss回调的接口
        @RequestMapping(value = "postObjectCallBack", method = RequestMethod.POST)
        public Object postObjectCallBack(HttpServletRequest request, @RequestBody Object callbackBody) throws IOException {
    
            log.info("---callbackBody={}",callbackBody);
    
    //        "{"filename":"test/comment/tt1.jpg","mimeType":"image/png"}"
             return callbackBody;
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    前端

    小程序
    const host = ''; //"https://examplebucket.oss-cn-hangzhou.aliyuncs.com"
    const signature = '';
    const ossAccessKeyId = '';
    const policy = '';
    const key = '';
    const securityToken = ''; 
    const filePath = ''; // 待上传文件的文件路径。
    wx.uploadFile({
      url: host, // 这个是阿里云bucket的根地址,使用这个地址拼接路径可以访问已上传的文件。如果是自己服务器则是开发者服务器的URL。
      filePath: filePath,// 本地文件路径,小程序chooseImage方法返回的路径
      name: 'file', // 必须填file。
      formData: {
        key,
        policy,
        OSSAccessKeyId: ossAccessKeyId,
        signature,
        // 'x-oss-security-token': securityToken // 使用STS签名时必传。
      },
      success: (res) => {
        if (res.statusCode === 204) {
          console.log('上传成功');
        }
      },
      fail: err => {
        console.log(err);
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    uniapp、uView的Upload组件
    uni.uploadFile({
        url: 'https://res.xxx.cn', //这个是阿里云bucket的根地址,使用这个地址拼接路径可以访问已上传的文件
        filePath: url,// 本地文件路径,小程序chooseImage方法返回的路径
        name: 'file',// 必须填file
        formData: {
            key: 'test/comment/tt1.jpg',//会把tt1.jpg图片上传至bucket(上方url所指向)的test/comment目录
            policy: this.postObject.policy,
            OSSAccessKeyId: this.postObject.accessKeyId,
            signature: this.postObject.signature,
            // callback: this.postObject.callback
        },
        success: (res) => {
            console.log('uni.uploadFile success:', res)
            if(res.statusCode===204){
                // 上传成功
                console.log('-------------success------------')
            }else if(res.statusCode===403){
                // Policy expired.
                uni.showToast({
                    title: '网络超时',
                    duration: 2000
                });
                // 续期
                this.getPostObjectParams()
            }else{
                console.log('上传失败')
            }
            // setTimeout(() => {
            resolve(res)
            // }, 1000)
        },
        fail(err) {
            console.error('uni.uploadFile: fail', err)
        }
    });
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    onLoad() {
    	this.getPostObjectParams()
    },
    ...........
    getPostObjectParams() {
        //从后台获取stsToken
        this.$post(GET_POST_OBJECT_PARAMS).then(rsp => {
            if (rsp.success) {
                this.postObject = rsp.data;
                console.log('this.postObject:', this.postObject)
            } else {
                uni.showToast({
                    title: rsp.message,
                    duration: 2000,
                    icon:'none'
                });
            }
        })
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    参考官方文档

    举一个uniapp例子

    UI库:uView
    在这里插入图片描述

    <template>
    	<!-- 发表评论 -->
    	<view class="create-comment">
    		<view class="star comment-common">
    			<view class="title">评分</view>
    			<view class="control">
    				<text class="name">游玩体验</text>
    				<u-rate :count="5" v-model="starCount" :touchable="false" active-color="#E65526" size="24"></u-rate>
    			</view>
    
    		</view>
    
    		<view class="content comment-common">
    			<view class="title">评价内容</view>
    			<textarea v-model="resourceComment.content" maxlength="200"
    				placeholder="游玩的满意吗?大家都想了解这里值得去吗?有什么亮点?期待你精彩的点评!">
    			</textarea>
    		</view>
    
    		<view class="picture comment-common">
    			<view class="title">图片</view>
    			<!-- name=1对应fileList1 -->
    			<u-upload :fileList="fileList1" @afterRead="afterRead" @delete="deletePic" name="1" multiple :maxCount="15"
    				:maxSize="maxSize">
    			</u-upload>
    		</view>
    
    		<view class="picture comment-common">
    			<view class="title">视频</view>
    			<!-- name=2对应fileList2 -->
    			<u-upload :fileList="fileList2" @afterRead="afterRead" @delete="deletePic" name="2" multiple :maxCount="1"
    				:maxSize="maxSize" accept="video" uploadIcon="movie"></u-upload>
    		</view>
    
    		<button @click="submit()" type="warn" class="submit" :loading="loading" :disabled="loading">发布</button>
    	</view>
    </template>
    
    <script>
    	import {
    		GET_POST_OBJECT_PARAMS
    	} from '../../api/api.js'
    
    	export default {
    		data() {
    			return {
    				starCount: 0,
    				resourceComment: {
    					content: ''
    				},
    				fileList1: [],
    				fileList2: [],
    				loading: false,
    				postObject: {
    					expire: 0
    				},
    				maxSize: 100 * 1024 * 1024
    			}
    		},
    		onLoad() {
    		},
    		methods: {
    			// -----upload start
    			// 新增图片
    			async afterRead(event) {
    				console.log('event:', event)
    
    				// 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    				let lists = [].concat(event.file)
    				// console.log('lists:', lists)
    				let fileListLen = this[`fileList${event.name}`].length
    				// console.log('fileListLen:', fileListLen)
    				lists.map((item) => {
    					this[`fileList${event.name}`].push({
    						...item,
    						status: 'uploading',
    						message: '上传中'
    					})
    				})
    
    				let time = new Date().getTime() / 1000
    				// console.log('time:', time)
    				if (time > this.postObject.expire) {
    					// policy过期,续期
    					await this.getPostObjectParams()
    				}
    
    				for (let i = 0; i < lists.length; i++) {
    					const result = await this.uploadFilePromise(lists[i].url)
    					let item = this[`fileList${event.name}`][fileListLen]
    					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
    						status: result ? 'success' : 'failed',
    						message: '',
    						url: result
    					}))
    					fileListLen++
    				}
    			},
    			compressJpgImage(src) {
    				return new Promise((resolve, reject) => {
    					// uni.compressImage({
    					//   src: src,
    					//   quality: 80,
    					//   success: res => {
    					//     console.log(res.tempFilePath)
    					//   }
    					// })
    				})
    			},
    			uploadFilePromise(url) {
    				return new Promise((resolve, reject) => {
    					let a = uni.uploadFile({
    						url: 'https://res.xxxx.cn',
    						filePath: url,
    						name: 'file', // 必须填file
    						formData: {
    							key: 'test/comment/tt3.jpg',
    							policy: this.postObject.policy,
    							OSSAccessKeyId: this.postObject.accessKeyId,
    							signature: this.postObject.signature,
    							callback: this.postObject.callback
    						},
    						success: (res) => {
    							// 未配置回调 上传成功返回 {date:"",errMsg:"uploadFile:ok",statusCode:204},如果配置了回调data参数才会有值
    							// 配置了回调 上传成功返回 {{data:{"filename":"test/comment/tt1.jpg","mimeType":"image/png"},errMsg:"uploadFile:ok",statusCode:200}
    							console.log('uni.uploadFile success():', res)
    							if (res.statusCode === 204 || res.statusCode === 200) {
    								// 上传成功
    								console.log('-------------uploaded success')
    								resolve(url)
    							} else {
    								console.log('-------------uploaded failed')
    								uni.showToast({
    									title: '上传失败',
    									duration: 2000,
    									icon: 'error'
    								});
    								resolve()
    							}
    						},
    						fail(err) {
    							console.error('uni.uploadFile: fail():', err)
    						}
    					});
    				})
    			},
    			// 删除图片
    			deletePic(event) {
    				this[`fileList${event.name}`].splice(event.index, 1)
    			},
    			// -----upload end
    
    			getPostObjectParams() {
    				//从后台获取postObject
    				return this.$post(GET_POST_OBJECT_PARAMS,{folderType:'comment'}).then(rsp => {
    					// this.postObject = rsp
    					if (rsp.success) {
    						this.postObject = rsp.data;
    						console.log('this.postObject:', this.postObject)
    					} else {
    						console.error('getPostObjectParams:', rsp.message || '系统错误')
    					}
    				})
    			}
    		}
    	}
    </script>
    
    <style lang="scss">
    	.create-comment {
    		padding: 12px;
    
    		.comment-common {
    			margin-bottom: 10px;
    			padding: 15px 10px;
    			background-color: white;
    			border-radius: 10px;
    		}
    
    		.title {
    			margin-bottom: 10px;
    			font-size: 16px;
    			font-weight: bold;
    		}
    
    		.star {
    			.control {
    				display: flex;
    				align-items: center;
    
    				.name {
    					margin-right: 10px;
    				}
    			}
    		}
    
    		.content {
    			textarea {
    				font-size: 14px;
    				width: 100%;
    			}
    		}
    
    		.picture {}
    
    		.submit {
    			margin-top: 20px;
    			width: 80%;
    			font-size: 15px;
    			color: white;
    			background-color: #e65526;
    		}
    	}
    </style>
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216

    附:根据blob链接获取blob对象

    			/**
    			 * 根据blob链接获取blob对象
    			 * @param {Object} url "blob:http://localhost:8085/d688ce4f-0f5d-418c-85ad-62bcb3f38dee"
    			 * @returns Blob(31846) {size: 31846, type: "image/jpeg"}
    			 */
    			getBlobByUrl(url) {
    				return uni.request({
    					url: url,
    					// 合法值:text、arraybuffer
    					responseType: 'arraybuffer'
    				}).then(data=>{
    					const [error, rsp]  = data;
    					if(error){
    						console.error(`post-error:${error}, url:${url}`)
    						return {message: error.errMsg}
    					}else{
    						let buffer = rsp.data
    						// ArrayBuffer(185) {}
    						console.log('buffer:', buffer)
    						return new Blob([buffer])
    					}
    				})
    				
    				// return new Promise((resolve, reject) => {
    					
    				// 	let xhr = new XMLHttpRequest()
    				// 	xhr.open('GET', url, true)
    				// 	xhr.responseType = 'blob'
    				// 	xhr.onload = function(e) {
    				// 		if (this.status == 200) {
    				// 			let myBlob = this.response
    
    				// 			// let file = new window.File(
    				// 			// 	[myBlob],
    				// 			// 	'myfile.jpg', {
    				// 			// 		type: myBlob.type
    				// 			// 	}
    				// 			// )
    				// 			// console.log("files:", file)
    				// 			resolve(myBlob)
    				// 		} else {
    				// 			reject(false)
    				// 		}
    				// 	}
    				// 	xhr.send()
    				// })
    			},
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    blobUrl、blob、base64、file相互转化:https://www.cnblogs.com/jing-zhe/p/15402775.html

    uniapp选择file

    <button @click="submit()" type="warn" class="submit" :loading="loading" :disabled="loading">发布</button>
    
    submit() {
        let utils = new Utils()
        uni.chooseImage({
            count: 6, //默认9
            sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
            sourceType: ['album'], //从相册选择
            success: function(res) {
                console.log(JSON.stringify(res.tempFilePaths));
                console.log(res.tempFiles)
    
                utils.getFileMD5(res.tempFiles[0], function(md5) {
                    console.log('md5:', md5)
                })
            }
        });
       
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    信息学奥赛一本通2062:【例1.3】电影票
    Spark中的Structured Streaming
    ArcGIS模拟风场(流场)
    集合的运算
    SQL优化--关联子查询的前世今生
    想要精通算法和SQL的成长之路 - 分发糖果
    MindSpore优秀论文5:[AAAI] CycleCol:基于循环卷积神经网络对真实单色-彩色摄像系统着色
    Python解析MDX词典数据并保存到Excel
    v-model的双向绑定如何通过v-bind和v-on手动实现
    从零开发一款图片编辑器Mitu-Dooring
  • 原文地址:https://blog.csdn.net/dan_seek/article/details/126588331