阿里云OSS临时凭证前后端配合上传文件

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
对象存储 OSS,恶意文件检测 1000次 1年
简介: 阿里云OSS临时凭证前后端配合上传文件

唔姆,分享一篇企业里用的阿里云OSS临时签证直传的上传教程

项目地址…springboot+thymeleaf+jquery,简单好用,写博客、做网站专属

https://github.com/VampireAchao/ossUpload

一般的阿里云OSS上传,要么放在前端上传,暴露了accesskey和secrekey

要么放在后端,文件先传到后端,再由后端上传,让服务器压力变得巨大。。。

所以,这里一篇服务端签名后直传的教程

Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。但本示例中的服务端无法实时了解用户上传了多少文件,上传了什么文件。

如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调

坏处还有就是辛苦了我们的前端同志。。。

首先开通服务

登录阿里云官网。
将鼠标移至产品,单击对象存储 OSS,打开 OSS 产品详情页面。
在 OSS 产品详情页,单击立即开通。
开通服务后,在 OSS 产品详情页单击管理控制台直接进入 OSS 管理控制台界面。
您也可以单击位于官网首页右上方菜单栏的控制台,进入阿里云管理控制台首页,然后单击左侧的对象存储 OSS 菜单进入 OSS 管理控制台界面。   //从官方文档复制的

创建一个springboot项目,勾选web和thymeleaf(导入js用到了一点)

然后导入依赖

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
    <version>2.2.0.RELEASE</version>    //版本可更改
</dependency>

编写配置

spring:
  cloud:
    alicloud:
      access-key: ACCESSKEY
      secret-key: SECRETKEY
      oss:
        endpoint: oss-cn-chengdu.aliyuncs.com
        bucket: waibi

然后调用,我这里用了个包装类,也一并贴上

package com.ruben.service.impl;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.PolicyConditions;
import com.ruben.common.json.AjaxJson;
import com.ruben.service.UploadService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
/**
 * @ClassName: UploadServiceImpl
 * @Description: 获取OSS签证
 * @Date: 2020/6/3 21:55
 * *
 * @author: achao<achao1441470436 @ gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
@Service
public class UploadServiceImpl implements UploadService {
    @Resource
    OSS ossClient;
    @Value("${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;
    @Value("${spring.cloud.alicloud.oss.bucket}")
    private String bucket;
    @Value("${spring.cloud.alicloud.access-key}")
    private String accessId;
    /**
     * 获取临时签证
     * @return
     */
    @Override
    public AjaxJson getMark() {
        String host = "https://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        // callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
//        String callbackUrl = "http://88.88.88.88:8888";
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String dir = format + "/"; // 用户上传文件时指定的前缀。
        Map<String, String> respMap = null;
        try {
            long expireTime = 3000;
            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, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            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));
        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        } finally {
            ossClient.shutdown();
        }
        return AjaxJson.success("获取签证成功!").put("data", respMap);
    }
}
/**
 * Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
 */
package com.ruben.common.json;
import com.fasterxml.jackson.annotation.JsonIgnore;
//import com.jeeplus.core.mapper.JsonMapper;
import org.springframework.http.HttpStatus;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
 * $.ajax后需要接受的JSON
 *
 * @author
 *
 */
public class AjaxJson extends HashMap<String,Object> implements Serializable {
  public AjaxJson(){
    this.put("success", true);
    this.put("code", HttpStatus.OK.value());
    this.put("msg", "操作成功");
  }
  public String getMsg() {
    return (String)this.get("msg");
  }
  public void setMsg(String msg) {//向json中添加属性,在js中访问,请调用data.msg
    this.put("msg", msg);
  }
  public boolean isSuccess() {
    return (boolean)this.get("success");
  }
  public void setSuccess(boolean success) {
    this.put("success", success);
  }
/*  @JsonIgnore//返回对象时忽略此属性
  public String getJsonStr() {//返回json字符串数组,将访问msg和key的方式统一化,都使用data.key的方式直接访问。
    String json = JsonMapper.getInstance().toJson(this);
    return json;
  }*/
  @JsonIgnore//返回对象时忽略此属性
  public static AjaxJson success(String msg) {
    AjaxJson j = new AjaxJson();
    j.setMsg(msg);
    return j;
  }
  @JsonIgnore//返回对象时忽略此属性
  public static AjaxJson error(String msg) {
    AjaxJson j = new AjaxJson();
    j.setSuccess(false);
    j.setMsg(msg);
    return j;
  }
  public static AjaxJson success(Map<String, Object> map) {
    AjaxJson restResponse = new AjaxJson();
    restResponse.putAll(map);
    return restResponse;
  }
  public static AjaxJson success() {
    return new AjaxJson();
  }
  @Override
  public AjaxJson put(String key, Object value) {
    super.put(key, value);
    return this;
  }
  public AjaxJson putMap(Map m) {
    super.putAll(m);
    return this;
  }
  public int getCode() {
    return (int)this.get("code");
  }
  public void setCode(int code) {
    this.put("code", code);
  }
}


顺便一提,这个包装类是jeeplus的,若本教程存在任何侵权问题,请联系我删除…

controller里就很简单,就一个方法

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
package com.ruben.controller;
import com.ruben.common.json.AjaxJson;
import com.ruben.service.UploadService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * @ClassName: UploadController
 * @Description: 文件上传controller
 * @Date: 2020/6/3 21:53
 * *
 * @author: achao<achao1441470436 @ gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
@RestController
@RequestMapping("oss")
publicclassUploadController {
@Resource
    UploadService uploadService;
/**
     * 获取签证
     *
     * @return
     */
@GetMapping("getMark")
public AjaxJson getMark() {
return uploadService.getMark();
    }
}

然后是前端的页面,记得导入thymeleaf

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>upload</title>
    <script type="application/javascript" th:src="@{/js/upload.js}"></script>
    <script type="text/javascript" src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<input type="file" id="uploadFile" multiple="multiple">
<script>
    var firstShowImgs = true; //是否第一次调用setShowFiles()这个函数
    // uploadFile input 框回调拿到的file对象
    $('#uploadFile').change(function (e) {
        // console.log(e.target.files);
        var uploadFile=e.target.files;
        var files=[];
        for(var i=0;i<uploadFile.length;i++){ //循环push包装一下file对象
            files.push({
                returnShow: false, //当前图片是否上传完成
                url: '', //url 地址
                file: uploadFile[i], //file 对象
                isDel:false //是否删除
            })
        }
        console.log(files);
        setShowFiles(0,files);
    });
</script>
</body>
</html>

PS:前端页面是让同事帮忙写的,自己JQuery忘完了,记得导入js

const baseUrl = 'http://localhost:8080';
const utils = {
    getUUID() { //生成UUID
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
            return (c === 'x' ? (Math.random() * 16 | 0) : ('r&0x3' | '0x8')).toString(16)
        })
    },
    getSuffix(fileName) { //获取文件后缀名
        var first = fileName.lastIndexOf(".");//取到文件名开始到最后一个点的长度
        var namelength = fileName.length;//取到文件名长度
        var filesuffix = fileName.substring(first + 1, namelength);//截取获得后缀名
        return `.${filesuffix}`
    },
    getfileName(fileName) { //获取文件名(不要后缀)
        var first = fileName.lastIndexOf(".");//取到文件名开始到最后一个点的长度
        var filesuffix = fileName.substring(0, first);//截取获得文件名
        return `${filesuffix.replace(/\s/g, "")}`
    },
    getFileUrlName(fileName) { //获取文件路径名称
        var first = fileName.lastIndexOf("/");//取到文件名开始到最后一个/的长度
        var namelength = fileName.length;//取到文件名长度
        var filesuffix = fileName.substring(first + 1, namelength);//截取获得文件名
        return `${filesuffix}`
    },
    getObjectURL(file) {  //返回文件本地路径
        var url = null;
        if (window.createObjectURL != undefined) {
            url = window.createObjectURL(file);
        } else if (window.URL != undefined) {
            url = window.URL.createObjectURL(file);
        } else if (window.webkitURL != undefined) {
            url = window.webkitURL.createObjectURL(file);
        }
        return url;
    }
}
function request(url, type, params = {}) {
    if (type == "URL_GET") {
        // console.log(params.url);
        url = url + '/' + params.url.join('/');
        delete (params['url']);
        type = 'GET';
    } else if (type == "URL_POST") {
        url = url + '/' + params.url.join('/');
        delete (params['url']);
        type = 'POST';
    }
    if (localStorage.getItem('token') && localStorage.getItem('refreshToken')) {
        params['headers'] = {//携带token
            "apiToken": localStorage.getItem('token'),
            "apiRefreshToken": localStorage.getItem('refreshToken')
        };
    }
    if (params['data'] && type == "POST") {
        params['data'] = JSON.stringify(params['data']);
    }
    return new Promise(function (resolve, reject) {
        $.ajax({
            url: baseUrl + url,
            type: type,
            ...params,
            contentType: 'application/json; charset=UTF-8',
            success: function (res) {
                if (res.code == 200) {
                    resolve(res)
                } else {
                }
            },
            error: function (res) {
                reject(res)
            }
        });
    })
}
function aliOssUploadFile(obj = {}) {
    // alert(obj);
    http.getOssInfo().then((res) => {
        if (res.code == 200 && res.success == true) {
            var config = res.data;
            var formData = new FormData();
            var filesAddress = `${config.dir}${utils.getfileName(obj.name)}-${utils.getUUID()}-${+new Date()}${utils.getSuffix(obj.name)}`;
            formData.append('key', filesAddress); //存储在oss的文件路径
            formData.append('ossaccessKeyId', config.accessid); //accessKeyId
            formData.append('policy', config.policy); //policy
            formData.append('Signature', config.signature); //签名
            formData.append("file", obj.file);
            formData.append("dir", config.dir);
            formData.append('success_action_status', 200); //成功后返回的操作码
            $.ajax({
                type: 'POST',
                data: formData,
                url: config.host,
                processData: false,
                contentType: false,
                async: true,
                xhr: function () {
                    myXhr = $.ajaxSettings.xhr();
                    if (myXhr.upload) { // check if upload property exists
                        myXhr.upload.addEventListener('progress', (e) => {
                            var loaded = e.loaded;                  //已经上传大小情况
                            var total = e.total;                      //附件总大小
                            var percent = Math.floor(100 * loaded / total) + "%";     //已经上传的百分比
                            // console.log("已经上传了:"+percent);
                            obj.success({
                                meCode: 201,
                                data: {total, loaded, percent: percent, num: Math.floor(100 * loaded / total)}
                            })
                        }, false); // for handling the progress of the upload
                    }
                    return myXhr;
                },
                success: (res) => {
                    obj.success({meCode: 200, data: {url: config.host + '/' + filesAddress}})
                },
                error: (err) => {
                    console.log(err);
                    obj.error({code: 200, success: false, msg: 'oss上传文件失败', data: err})
                }
            })
        } else {
            obj.error(res);
        }
    }).catch((err) => {
        obj.error({code: 200, success: false, msg: '获取oss签证失败', data: err});
    })
}
const http = {
    uploadFile: aliOssUploadFile, //阿里云oss上传文件
    getOssInfo: (params) => request('/oss/getMark', 'GET', params), //获取oss信息
}
// var firstShowImgs = true; //是否第一次调用setShowFiles()这个函数
// uploadFile input 框回调拿到的file对象
// for(var i=0;i<uploadFile.length;i++){ //循环push包装一下file对象
//   files.push({
//     returnShow: false, //当前图片是否上传完成
//     url: utils.getObjectURL(uploadFile[i]), //url 地址
//     file: uploadFile[i], //file 对象
//     isDel:false //是否删除
//   })
// }
// setShowFiles(0,files);
function setShowFiles(index, files) { //循环多文件上传
    if (index > files.length - 1) { //结束递归
        console.log(files);
        var arrImg = [];
        files.forEach(item => {
            if (item.returnShow == true && !item.isDel) {
                arrImg.push(item.url);
                item.isDel = true;
            }
        });
        console.log(arrImg); //结束递归时返回所有上传成功的url数组
        firstShowImgs = true; //改为初始值。
        return false
    }
    if (files[index].returnShow || files[index].isDel) {
        setShowFiles(index - 0 + 1, files); //递归下一个
        return false
    }
    http.uploadFile({ //上传
        file: files[index].file, //文件
        name: files[index].file.name, //文件名称
        success: (res) => { //成功返回
            if (res.meCode == 200) { //成功
                files[index].returnShow = true;
                files[index].url = res.data.url;
                setShowFiles(index - 0 + 1, files); //递归
            }
        },
        error: (err) => { //失败返回
            files[index].isDel = true; //,当前图片上传失败后改为删除状态就不得再次上传
            setShowFiles(index - 0 + 1, files); //递归下一个
            console.log(err);
        }
    })
}

然后把文件拖到上传按钮那里就上传了,路径拼到了控制台打印出来了

之后可以传到服务端保存到数据库里,这样就完成了保存路径的操作

相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
4月前
|
机器学习/深度学习 人工智能 专有云
人工智能平台PAI使用问题之怎么将DLC的数据写入到另一个阿里云主账号的OSS中
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
16天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
3月前
|
存储 机器学习/深度学习 弹性计算
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
|
4月前
|
消息中间件 分布式计算 DataWorks
DataWorks产品使用合集之如何使用Python和阿里云SDK读取OSS中的文件
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
4月前
|
存储 运维 安全
阿里云OSS的优势
【7月更文挑战第19天】阿里云OSS的优势
189 2
|
4月前
|
存储 API 开发工具
阿里云OSS
【7月更文挑战第19天】阿里云OSS
187 1
|
4月前
|
存储 弹性计算 对象存储
预留空间是什么?阿里云OSS对象存储预留空间说明
阿里云OSS预留空间是预付费存储产品,提供折扣价以锁定特定容量,适用于抵扣有地域属性的Bucket标准存储费用及ECS快照费。通过购买预留空间,如500GB通用预留+100GB标准-本地冗余存储包,用户可优化成本。
203 4
|
4月前
|
人工智能 对象存储
【阿里云AI助理】自家产品提供错误答案。阿里云OSS 资源包类型: 下行流量 地域: 中国内地通用 下行流量包规格: 300 GB 套餐: 下行流量包(中国内地) ,包1年。那么这个是每月300GB,1年是3600GB的流量;还是1年只有300GB的流量?
自家产品提供错误答案。阿里云OSS 资源包类型: 下行流量 地域: 中国内地通用 下行流量包规格: 300 GB 套餐: 下行流量包(中国内地) ,包1年。那么这个是每月300GB,1年是3600GB的流量;还是1年只有300GB的流量?
128 1
|
4月前
|
持续交付 开发工具 对象存储
阿里云云效产品使用合集之构建物如何上传到阿里云OSS
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
4月前
|
缓存 对象存储 数据安全/隐私保护
阿里云OSS, 跨域请求, No ‘Access-Control-Allow-Origin‘
阿里云OSS, 跨域请求, No ‘Access-Control-Allow-Origin‘
716 0

热门文章

最新文章