Web端文件直传oss存储空间

Web端文件直传oss存储空间

lookroot 442 2022-05-10

之前的学习手记,有朋友需要,现在同步过来,请注意时效性!

什么是网页直传oss

传统的网页传输文件都会先把文件传输到后端服务器,再由后端服务器上传到oss空间

image-20210307140113581

这样做的缺点是:

  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。

我们可以直接在web端、小程序将文件直传oss空间,本期通过阿里云oss来作为案例展示

STS临时授权访问OSS

因为是网页端直传,所以网页中会保存我们的oss账户信息,这样是不安全的,推荐使用sts临时授权的方式来完成交互 文档地址

创建子用户

首先创建一个ram用户,勾选编程访问,创建好以后保存id和key

image-20210307140742858

选中这个用户,点击添加权限,添加AliyunSTSAssumeRoleAccess这个权限,点击确定

image-20210307140925376

创建权限策略

创建完用户以后,我们来创建一个策略,在策略中可以限定可以使用的权限和可以操作的文件夹

更多的策略配置可以查看RAM Policy概述

image-20210307141349394

脚本

{
    "Version": "1",
    "Statement": [
     {
           "Effect": "Allow",
           "Action": [
             "oss:PutObject"
           ],
           "Resource": [
             "acs:oss:*:*:loop"
           ]
     }
    ]
}

创建角色

点击Ram角色管理,点击创建Ram角色,点击阿里云账号

image-20210307141724917

点击完成以后,点击为角色授权,然后点击刚刚创建的权限策略

image-20210307141831047

然后点击进入这个角色,保存ARN

image-20210307141945761

获取token

创建一个springboot项目并添加依赖

 <dependency>
	<groupId>com.aliyun</groupId>
	<artifactId>aliyun-java-sdk-sts</artifactId>
	<version>3.0.0</version>
</dependency>
<dependency>
	<groupId>com.aliyun</groupId>
	<artifactId>aliyun-java-sdk-core</artifactId>
	<version>4.4.6</version>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-configuration-processor</artifactId>
	<optional>true</optional>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

修改application.yml,填入自己的配置文件

alioss:
  endpoint: oss-cn-chengdu.aliyuncs.com
  accessKeyId: 
  accessKeySecret: 
  roleArn: 
  regionID: cn-chengdu
  bucket: loop
spring:
  thymeleaf:
    cache: false

创建配置类

@Component
@ConfigurationProperties(prefix = "alioss")
@Data
public class AliOssConfig {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String roleArn;
    private String regionID;
    private String bucket;
}

创建一个前端需要的对象OssTokenVO

@Data
@Builder
public class OssTokenVO {
    private String accessKeyId;
    private String accessKeySecret;
    private String securityToken;
    private String region;
    private String bucket;
}

创建service AliOssService

@Service
public class AliOssService {
    @Autowired
    private AliOssConfig aliOssConfig;

    /**
     * 获取直传token
     *
     * @return
     */
    public OssTokenVO getDirectToken() throws ClientException {
        //构造请求器
        IClientProfile profile = DefaultProfile.getProfile(aliOssConfig.getRegionID(),
                aliOssConfig.getAccessKeyId(),
                aliOssConfig.getAccessKeySecret());
        DefaultAcsClient client = new DefaultAcsClient(profile);
        AssumeRoleRequest request = new AssumeRoleRequest();
        request.setRoleSessionName("loop");
        request.setRoleArn(aliOssConfig.getRoleArn());
        request.setDurationSeconds(1000L);
        //发送请求
        AssumeRoleResponse acsResponse = client.getAcsResponse(request);
        AssumeRoleResponse.Credentials credentials = acsResponse.getCredentials();
        //组装一个web需要的对象
        String accessKeyId = credentials.getAccessKeyId();
        String accessKeySecret = credentials.getAccessKeySecret();
        String securityToken = credentials.getSecurityToken();
        return OssTokenVO.builder()
                .securityToken(securityToken)
                .accessKeyId(accessKeyId)
                .accessKeySecret(accessKeySecret)
                .region("oss-" + aliOssConfig.getRegionID())
                .bucket(aliOssConfig.getBucket())
                .build();
    }
}

简单写个控制器

@RestController
@RequestMapping("/oss")
public class OssController {
    @Autowired
    private AliOssService aliOssService;

    @GetMapping("/token")
    public OssTokenVO test() throws ClientException {
        return aliOssService.getDirectToken();
    }
}

测试一下

image-20210307155829092

编写页面测试

编写一个测试页面index.html,加个控制器跳转过去

public class IndexController {
    @GetMapping
    public ModelAndView index(ModelAndView modelAndView) {
        modelAndView.setViewName("index");
        return modelAndView;
    }
}

引入依赖 aliyun-oss-sdk文档

<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.13.2.min.js"></script>

写个选择文件

<input type="file" id="ossInput">

上传逻辑

document.getElementById("ossInput")
    .addEventListener("input", (e) => {
        //请求后端拿到token
        axios.get('/oss/token')
            .then(function (response) {
                let ossConfig = response.data;
                //实例化一个client
                let client = new OSS({
                    region: ossConfig.region,
                    accessKeyId: ossConfig.accessKeyId,
                    accessKeySecret: ossConfig.accessKeySecret,
                    stsToken: ossConfig.securityToken,
                    bucket: ossConfig.bucket
                });
                //上传文件
                client.put("test.png", e.target.files[0]);
            })
    })

设置oss存储空间的Headers

设置一下跨域

image-20210307155424544

完成编写,此时点击选择文件上传便可以 直接上传到oss存储空间了