Java-整合OSS

文章目录

  • 前言
  • 一、OSS 简介
  • 二、OSS 的使用
    • 1. Bucket 的创建与文件上传
    • 2. 创建 RAM 与用户授权
    • 3. 图形化管理工具-ossbrowser
  • 三、Java 整合 OSS
    • 1. 基本实现
    • 2. 客户端直传


前言

最近公司的技术负责人让我整合下 OSS 到项目中,所以花了一点时间研究了下 OSS,虽然说在 OSS 的官方文档中有如何整合 OSS 的详细说明,但是不得不说文档实在是太详细了,如果仅仅是通过看官方文档去整合,可能会看到太多暂时用不上的内容,所以我简化下文档中的内容,也是谨防日后忘记,故此作为分享。

一、OSS 简介

阿里云对象存储 OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,提供最高可达 99.995 % 的服务可用性。多种存储类型供选择,全面优化存储成本。

可以在阿里云的产品列表中找到

地址:https://www.aliyun.com/

在这里插入图片描述

在这里插入图片描述

如果只是想玩一玩,做技术扩展用的,可以买一个商品类型为 OSS 资源包 的,价格很便宜

在这里插入图片描述

对于整合这些第三方的技术,最重要的就是学会去看这些第三方提供的文档

在这里插入图片描述
约莫看一下,大概就知道这些技术是什么?怎么用的

对象存储 OSS 产品文档:https://help.aliyun.com/zh/oss/

在这里插入图片描述

从产品文档中可以看到 OSS 的工作原理:

数据以对象(Object)的形式存储在 OSS 的存储空间(Bucket )中。如果要使用 OSS 存储数据,需要先创建 Bucket,并指定Bucket的地域、访问权限、存储类型等属性。创建 Bucket 后,您可以将数据以 Object 的形式上传到 Bucket,并指定 Object 的文件名(Key)作为其唯一标识。

至少需要了解如下几个概念:

  • 存储空间(Bucket):存储空间是用户用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。存储空间具有各种配置属性,包括地域、访问权限、存储类型等。用户可以根据实际需求,创建不同类型的存储空间来存储不同的数据。
  • 对象(Object):对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。和传统的文件系统不同,对象没有文件目录层级结构的关系。对象由元信息(Object Meta)、用户数据(Data)和文件名(Key)组成,并且由存储空间内部唯一的 Key 来标识。
  • Region(地域):Region表示OSS的数据中心所在物理位置。用户可以根据费用、请求来源等选择合适的地域创建 Bucket。一般来说,距离用户更近的Region访问速度更快。Region 是在创建 Bucket 的时候指定的。Bucket 创建成功后,Region 将无法修改。
  • Endpoint(访问域名):Endpoint 表示 OSS 对外服务的访问域名。
  • AccessKey(访问密钥):AccessKey 简称 AK,指的是访问身份验证中用到的 AccessKey ID 和 AccessKey Secret。OSS 通过使用 AccessKey ID 和 AccessKey Secret 对称加密的方法来验证某个请求的发送者身份。AccessKey ID 用于标识用户;AccessKey Secret 是用户用于加密签名字符串和 OSS 用来验证签名字符串的密钥,必须保密。

OSS 产品文档 中有详细的说明,我就不多做赘述了。


二、OSS 的使用


1. Bucket 的创建与文件上传

购买 OSS 之后,登录阿里云账号,可在 产品与服务 中找到所购买的 对象存储 OSS

在这里插入图片描述

进入 对象存储 OSS

在这里插入图片描述

点击左侧菜单的 Bucket列表 ,就可以新建 Bucket

在这里插入图片描述

注意:Bucket 新建之后不可修改

在这里插入图片描述

创建成功后可在 Bucket 列表 中看到所创建的 Bucket

在这里插入图片描述

点击该 Bucket 就可以查看其下的 文件列表 与上传文件了

在这里插入图片描述

上传文件完成后会在 文件列表 中展示,可点击详情查看文件下载的 URL

在这里插入图片描述

在这里插入图片描述

可以看到这个 url 的组成:https://bucket.endpoint/filePath


2. 创建 RAM 与用户授权

在网页上虽然可以做这些文件上传等操作,但是需要登录本人的阿里云账号是不安全的,其次是在开发中也不可能在这上面进行操作,都是通过 API 进行文件上传下载等操作。

从上面的 OSS 介绍可知,OSS 的文件是存储在 Bucket 中,如果想要通过程序长期访问 Bucket 下的指定资源,就需要创建 RAM 用户(可以不登录阿里云主账号就能使用指定权限的功能),从而获取 AccessKeyIdAccessKeySecret 作为访问 OSS凭证

关于 RAM 的详细作用可参见 访问控制-RAM用户概览

(1)创建 RAM 用户

  • ① 登录阿里云,找到 AccessKey 管理,点击进入 RAM 控制台,在左侧导航栏,选择 身份管理 > 用户

在这里插入图片描述

  • ② 点击 创建用户 ,输入 登录名称显示名称,访问方式勾选 OpenAPI 调用访问

在这里插入图片描述

进行验证之后

在这里插入图片描述

就能获得 AccessKey IDAccessKey Secret,这个是使用 API 连接 OSS 需要用到的,一定要及时保存 AccessKey 的信息,页面关闭后将无法再次获取信息

(2)RAM 用户分配权限

  • ① 进入 RAM 控制台,在左侧导航栏,选择 身份管理 > 用户,可以看到所有创建好的 RAM 用户
  • ② 在用户页面,单击 目标RAM用户 操作列的 添加权限

在这里插入图片描述

  • ③ 在添加权限面板,为 RAM 用户添加权限

在这里插入图片描述

可以看到 选择权限 这里有很多条,针对控制访问 OSS,只需要勾选 AliyunOSSFullAccess 这个权限,点击保存即可。

在这里插入图片描述

关于 RAM 用户授权 详细说明可参见:为 RAM 用户授权


3. 图形化管理工具-ossbrowser

ossbrowser 是阿里云官方提供的 OSS 图形化管理工具,提供类似 Windows 资源管理器的功能。使用 ossbrowser,您可以快速完成存储空间(Bucket)和文件(Object)的相关操作。

下载:

官方下载地址:https://help.aliyun.com/zh/oss/developer-reference/install-and-log-on-to-ossbrowser

在这里插入图片描述

这里以 windows 64 为例,则下载 Windows 64 下的 oss-browser-win32-x64.zip 压缩包

安装与使用:

下载完成之后,进行解压,在解压后的 oss-browser-win32-x64 文件夹下找到 oss-browser.exe,双击即可打开

在这里插入图片描述

输入之前创建 RAM 用户 时所获取的 AccessKey 账号信息,并且该账户要有访问控制 OSS 的权限(AliyunOSSFullAccess),登入

在这里插入图片描述

在该软件上也可以进行文件上传下载等操作

在这里插入图片描述

我使用这个图形化管理工具的目的主要是为了测试 AccessKey 信息是否能正常连接访问 OSS ~~


三、Java 整合 OSS

关于如何使用 Java 整合 OSS 其实在 OSS 产品文档 中也有比较详细的说明,我们只需要参照 开发参考 中内容基本上都能实现。

在这里插入图片描述

所以我只提供常用的一些常用的功能实现,如果以下内容无法实现你目前的需要,可参考 OSS 产品文档 进行代码编写会比较稳妥。


1. 基本实现

首先是需要安装 OSS 的 Java SDK,在 Maven 工程中只需要在 pom.xml 中加入响应的依赖即可

(1)引入依赖

3.15.1 版本为例,在 <dependencies> 中加入如下内容:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.15.1</version>
</dependency>

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

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>2.3.3</version>
</dependency>

(2)添加配置文件

想要通过程序的方式去连接 OSS,那就必须告诉程序要连接哪个 OSS 端点、哪个 Bucket、并且告诉 OSS 你是谁,交出你的访问凭证

在这里插入图片描述

所以就需要配置 endpointbucketName 以及 AccessKey IdAccessKey Secret 信息,

endpointbucketName 可以在 Bucket 的详情页面获得

在这里插入图片描述

这部分内容一般会放在配置文件中,例如:

yml 文件配置

aliyun:
  oss:
    access-key-id: YOUR_ACCESS_KEY_ID
    access-key-secret: YOUR_ACCESS_KEY_SECRET
    endpoint: oss-cn-xxxxxx.aliyuncs.com
    bucket-name: mike-system-file

配置类 OssProperty.java

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties("aliyun.oss")
public class OssProperty {

    /**
     * AccessKey ID
     */
    private String accessKeyId;

    /**
     * AccessKey Secret
     */
    private String accessKeySecret;

    /**
     * endpoint
     */
    private String endpoint;

    /**
     * bucketName
     */
    private String bucketName;
}

(3)代码编写

OssService.java

import com.aliyun.oss.model.Bucket;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.Map;

public interface OssService {

    public static final String HTTPS = "https://";
    public static final String DOT = ".";
    public static final String FORWARD_SLASH = "/";

    /**
     * 列举存储空间
     */
    List<Bucket> showBuckets();

    /**
     * 创建存储空间
     */
    void createBucket(String bucketName);

    /**
     * 删除储存空间
     */
    void removeBucket(String bucketName);

    /**
     * 上传文件
     * @param dir 存储空间某文件夹下,例如:app
     * @param file 上传的文件
     * @return 可访问的路径
     */
    String upload(String dir, MultipartFile file);

    /**
     * 下载文件
     * @param filePath 文件存储全路径,例如:dir/filename(不带 bucket 名称)
     */
    void download(String filePath);

    /**
     * 删除文件
     * @param filePath 文件存储全路径,例如:dir/filename(不带 bucket 名称)
     */
    boolean remove(String filePath);
}

OssServiceImpl.java

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import com.aliyuncs.exceptions.ClientException;
import com.fsy.common.core.exception.CustomException;
import com.fsy.common.core.utils.DateUtils;
import com.fsy.common.core.utils.ServletUtils;
import com.fsy.tool.config.OssProperty;
import com.fsy.tool.listener.OssProgressListener;
import com.fsy.tool.service.OssService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OssServiceImpl implements OssService {

    private final OssProperty ossProperty;
    private final HttpServletResponse response;

    /**
     * 获取 OSSClient 实例
     */
    private OSS getOssClient() {
        String endpoint = ossProperty.getEndpoint();
        return new OSSClientBuilder().build(endpoint, ossProperty.getAccessKeyId(), ossProperty.getAccessKeySecret());
    }

	/**
     * 列举存储空间
     */
    @Override
    public List<Bucket> showBuckets() {
        OSS ossClient = getOssClient();
        try {
            // 列举当前账号所有地域下的存储空间
            return ossClient.listBuckets();
        } catch (OSSException e) {
            printlnException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }

	/**
     * 创建存储空间
     */
    @Override
    public void createBucket(String bucketName) {
        OSS ossClient = getOssClient();
        try {
            // 创建CreateBucketRequest对象。
            CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);

            // 如果创建存储空间的同时需要指定存储类型、存储空间的读写权限、数据容灾类型, 请参考如下代码
            // 此处以设置存储空间的存储类型为标准存储为例介绍
            //createBucketRequest.setStorageClass(StorageClass.Standard);
            // 数据容灾类型默认为本地冗余存储,即 DataRedundancyType.LRS。如果需要设置数据容灾类型为同城冗余存储,请设置为DataRedundancyType.ZRS
            //createBucketRequest.setDataRedundancyType(DataRedundancyType.ZRS);
            // 设置存储空间读写权限为公共读,默认为私有
            //createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
            // 在支持资源组的地域创建Bucket时,您可以为Bucket配置资源组。
            //createBucketRequest.setResourceGroupId(rsId);

            // 创建存储空间
            ossClient.createBucket(createBucketRequest);
        } catch (OSSException e) {
            printlnException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

	/**
     * 删除储存空间
     */
    @Override
    public void removeBucket(String bucketName) {
        OSS ossClient = getOssClient();
        try {
            // 删除存储空间
            ossClient.deleteBucket(bucketName);
        } catch (OSSException e) {
            printlnException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

	/**
     * 上传文件
     * @param dir 存储空间某文件夹下,例如:app
     * @param file 上传的文件
     * @return 可访问的路径
     */
    @Override
    public String upload(String dir, MultipartFile file) {

        // 获取文件名称
        String sourceName = file.getOriginalFilename();

        // 获取地域节点
        String endpoint = ossProperty.getEndpoint();
        // 获取存储空间名称
        String bucketName = ossProperty.getBucketName();
        // 当前日期
        String ymd = DateUtils.parseDateToStr(DateUtils.YYMMDD, new Date());
        // 文件存放地址(不带 bucket)
        String filePath;
        if (StringUtils.isNotBlank(dir)) {
            // 例如:app/ymd/wms.apk
            filePath = dir + FORWARD_SLASH + ymd + FORWARD_SLASH + sourceName;
        } else {
            filePath = ymd + FORWARD_SLASH + sourceName;
        }

        // 访问路径:https://bucket.endpoint/filePath
        String urlPath = HTTPS + bucketName + DOT +  endpoint + FORWARD_SLASH + filePath;
        // 相对路径
        String relativePath = FORWARD_SLASH + filePath;

        OSS ossClient = getOssClient();

        try {
            // 判断 bucket 是否存在
            if (!ossClient.doesBucketExist(bucketName)) {
                throw new CustomException("存储空间不存在");
            }
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, filePath, file.getInputStream());
            ObjectMetadata metadata = new ObjectMetadata();

            /*
             * 指定存储类型:
             *      对于任意存储类型的Bucket,如果上传Object时指定此参数,则此次上传的Object将存储为指定的类型
             * 取值:
             *      Standard:标准存储
             *      IA:低频访问
             *      Archive:归档存储
             *      ColdArchive:冷归档存储
             *      DeepColdArchive:深度冷归档存储
             */

            // 设置存储类型:标准存储(默认标准存储)
            metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());

            /*
             * 指定上传文件的访问权限:
             * 取值:
             *      default(默认):Object遵循所在存储空间的访问权限
             *      private:私有
             *      public-read:公共读
             *      public-read-write:公共读写
             */

            // 设置访问权限:默认(遵循所在存储空间的访问权限)
            metadata.setObjectAcl(CannedAccessControlList.Default);

            /*
             * 指定上传文件操作时是否覆盖同名 Object:
             *      不指定 x-oss-forbid-overwrite 时,默认覆盖同名 Object
             *      指定 x-oss-forbid-overwrite 为 false 时,表示允许覆盖同名 Object
             *      指定 x-oss-forbid-overwrite 为 true 时,表示禁止覆盖同名 Object,如果同名 Object 已存在,程序将报错
             */

            // 设置禁止覆盖同名文件
            metadata.setHeader("x-oss-forbid-overwrite", "false");

            // 设置元数据
            putObjectRequest.setMetadata(metadata);

            // 上传文件
            ossClient.putObject(putObjectRequest);

        } catch (IOException e) {
            log.error("failed to upload file.detail message:{}", e.getMessage());

        } catch (OSSException e) {
            printlnException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return urlPath;
    }

	/**
     * 下载文件
     * @param filePath 文件存储全路径,例如:dir/filename(不带 bucket 名称)
     */
    @Override
    public void download(String filePath) {

        String bucketName = ossProperty.getBucketName();
        OSS ossClient = getOssClient();

        // 截取文件名称
        String fileName = filePath.substring(filePath.lastIndexOf(FORWARD_SLASH));

        try {
            // 判断文件是否存在
            if (!ossClient.doesObjectExist(bucketName, filePath)) {
                throw new CustomException("文件不存在");
            }

            // ossObject 包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流
            OSSObject ossObject = ossClient.getObject(new GetObjectRequest(bucketName, filePath));
            InputStream inputStream = ossObject.getObjectContent();

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int num;
            while ((num = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, num);
            }
            byteArrayOutputStream.flush();
            byte[] bytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.close();

            // 读取,返回
            ServletUtils.writeAttachment(response, fileName, bytes);

            // ossObject 对象使用完毕后必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作
            ossObject.close();

        } catch (OSSException oe) {
            printlnException(oe);
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    @Override
    public boolean remove(String filePath) {
        String bucketName = ossProperty.getBucketName();

        OSS ossClient = getOssClient();
        try {
            // 判断文件是否存在
            if (!ossClient.doesObjectExist(bucketName, filePath)) {
                log.warn("need delete file:{} not exists", filePath);
                return false;
            }
            // 删除文件或目录。如果要删除目录,目录必须为空
            ossClient.deleteObject(bucketName, filePath);

            return true;

        } catch (OSSException oe) {
            printlnException(oe);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return false;
    }

	/**
	 * 返回附件
	 */
	public void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
        // 设置 header 和 contentType
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        // 输出附件
        IoUtil.write(response.getOutputStream(), false, content);
    }

    /**
     * 打印异常日志
     */
    public void printlnException(Exception e) {
        if (e instanceof OSSException) {
            OSSException oe = (OSSException) e;
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        }
        if (e instanceof ClientException) {
            ClientException ce = (ClientException) e;
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        }
    }
}

(4)测试

在这里插入图片描述

上传文件

在这里插入图片描述

在这里插入图片描述

以上便是 Java 对 OSS 的简单整合全部内容。

OSS 产品文档 中还有很多个案例,写得也是比较详细,我就不多做赘述了,只要先实现以上的功能,其它的都可以参照产品文档慢慢研究,比如说分片上传、进图条等等。

在这里插入图片描述


2. 客户端直传

除了通过服务器代理上传文件的方式外,OSS 还提供了客户端直传的方式。

在典型的服务端和客户端架构下,常见的文件上传方式是服务端代理上传:客户端将文件上传到业务服务器,然后业务服务器将文件上传到OSS。在这个过程中,一份数据需要在网络上传输两次,会造成网络资源的浪费、增大服务端的资源开销。为了解决这一问题,可以在客户端直连 OSS 来完成文件上传,无需经过业务服务器中转。

在这里插入图片描述

服务端代理上传和客户端直传相比,有以下三个缺点:

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

从服务端代理上传的案例可知,要想要上传图片,那就必须提供 endpointbucketAccessKey 的信息,但是在前端直接将这些信息写在 js 里面是非常不安全的,容易造成信息泄漏,遭受攻击

所以通常的做法就是前端先向后端发送上传文件的 Post Policy 请求,应用服务器返回签名给前端,前端再携带签名直接将文件上传至 OSS

在这里插入图片描述

后端可以参照 OSS 产品文档 来编写接口,提供签名

在这里插入图片描述

例如(在 基本实现 的案例上添加代码):

Controller

    @GetMapping(value = "/signature")
    @ApiOperation(value = "获取签名")
    public ResponseBean signature(@ApiParam(required = true, value = "上传路径") @RequestParam(required = true) String dir) {
        return ResponseBean.success(ossService.signature(dir));
    }

Service

    /**
     * 服务端签名直传
     * @param dir 设置上传到 OSS 的路径(目录)
     * @return 签名信息
     */
    Map<String, String> signature(String dir);

ServiceImpl

    @Override
    public Map<String, String> signature(String dir) {

        String accessId = ossProperty.getAccessKeyId();
        String endpoint = ossProperty.getEndpoint();
        String bucket = ossProperty.getBucketName();
        // Host 地址,格式为:https://bucket.endpoint
        String host = HTTPS + bucket + DOT +  endpoint;
        // 设置上传回调 URL
        String callbackUrl = "https://www.xxxx.xxx";

        // 创建ossClient实例
        OSS ossClient = getOssClient();
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConditions = new PolicyConditions();
            policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);

            String postPolicy = ossClient.generatePostPolicy(expiration, policyConditions);
            byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);

            Map<String, String> respMap = new LinkedHashMap<>();
            respMap.put("access_id", accessId);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(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("callbackBodyType", "application/x-www-form-urlencoded");
             *      String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes());
             *      respMap.put("callback", base64CallbackBody);
             */
            
            return respMap;

        } catch (Exception e) {
            // Assert.fail(e.getMessage());
            System.out.println(e.getMessage());
        }
        return null;
    }

Body中的各字段说明如下:

字段描述
accessid用户请求的AccessKey ID
host用户发送上传请求的域名
policy用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。详情请参见Post Policy
signature对Policy签名后的字符串
expire由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)
dir限制上传的文件前缀

测试:

在这里插入图片描述

后端就这样写就行了,

前端 vue + element-ui,OSS 上传文件代码如下:

...

未完待续...


参考博客

JAVA整合阿里云OSS/VUE上传阿里云OSS:https://blog.51cto.com/u_15899048/5903392

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/152780.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis内存淘汰机制

Redis内存淘汰机制 引言 Redis 启动会加载一个配置&#xff1a; maxmemory <byte> //内存上限 默认值为 0 (window版的限制为100M)&#xff0c;表示默认设置Redis内存上限。但是真实开发还是需要提前评估key的体量&#xff0c;提前设置好内容上限。 此时思考一个问题…

高质量发展项目——党务工作者能力提升培训在京成功举办

2021年6月&#xff0c;国务院办公厅印发了《关于推动公立医院高质量发展的意见》。为认真贯彻落实公立医院党建工作重点任务&#xff0c;加强公立医院党建&#xff0c;健全现代医院管理制度&#xff0c;实行党委领导下的院长负责制。发挥院级党组织把方向、管大局、作决策、促改…

flutter仿支付宝余额宝年化收益折线图

绘制: 1.在pubspec.yaml中引入:fl_chart: 0.55.2 2.绘制: import package:jade/utils/JadeColors.dart; import package:util/easy_loading_util.dart; import package:fl_chart/fl_chart.dart; import package:flutter/material.dart; import package:flutter_screenutil/…

MySQL分页查询的工作原理

前言 MySQL 的分页查询在我们的开发过程中还是很常见的&#xff0c;比如一些后台管理系统&#xff0c;我们一般会有查询订单列表页、商品列表页等。 示例&#xff1a; SELECT * FROM goods order by create_time limit 0,10; 在了解order by和limit的工作原理之前&#xff0c…

[DB] (数据库工具) navicat 平替 jookdb

jookdb 官方下载 csdn下载(免积分) 解压后直接可以使用 测试数据库适配性. mysql.mariadb.oracle.sqlserver免费使用 除外的提供20天免费试用驱动添加是java的jdbc驱动,可以通过 https://developer.aliyun.com/mvn/view 进行下载数据库的基本操作.包含数据库的新增, 库表新增…

SQLSERVER 遍历循环的两种方式很详细有源码(1)

1.普通循环 Create table WS_Student ( [Id] int primary key not null, [My_Cocode] [int], [My_SCocode] [int], [userId] [bigint], [SetCName] [varchar](50) NULL, [SetEName] [varchar](50) NULL, [SetPcode] [varchar](50) NULL, [Se…

单相浪涌保护器和三相浪涌保护器的区别

浪涌保护器&#xff0c;也称为防雷器&#xff0c;是一种为各种电子设备、仪器仪表、通讯线路提供安全防护的电子装置&#xff0c;主要用于限制过电压和泄放电涌电流。浪涌保护器的核心元件是内部的一个非线性元件。根据非线性元件的不同&#xff0c;浪涌保护器可以分为开关型&a…

​如何使用ArcGIS Pro制作渐变河流效果

对于面要素的河流水系&#xff0c;制作渐变效果方法比较简单&#xff0c;如果是线要素的河流有办法制作渐变效果吗&#xff0c;答案是肯定的&#xff0c;这里为大家介绍一下制作方法&#xff0c;希望能对你有所帮助。 数据来源 本教程所使用的数据是从水经微图中下载的水系数…

kubernetes部署jenkins

参考&#xff1a; ​​​​​​第七篇&#xff1a;kubernetes部署jenkins-CSDN博客 1、当前kubernetes集群已部署nfs服务 showmount -e 创建jenkins目录 2、添加jenkins的pvc cd /opt/dockerfile/jenkins/ touch jenkins-pv.yaml apiVersion: v1 kind: PersistentVolume m…

软件测试/人工智能丨深入人工智能软件测试:PyTorch引领新时代

在人工智能的浪潮中&#xff0c;软件测试的角色变得愈发关键。本文将介绍在人工智能软件测试中的一些关键技术&#xff0c;以及如何借助PyTorch深度学习框架来推动测试的创新与升级。 PyTorch&#xff1a;深度学习的引擎 PyTorch作为一种开源的深度学习框架&#xff0c;为软件…

【2021集创赛】Arm杯二等奖-基于Arm核的智慧病房手势识别方案

团队介绍 参赛单位&#xff1a;上海交通大学 队伍名称&#xff1a;芯灵手巧 指导老师&#xff1a;王琴、景乃锋 参赛队员&#xff1a;林圣凯、林新源、莫志文 总决赛奖项&#xff1a;二等奖 1.项目概述 1.1 选题背景 我们的选题背景是考虑到很多卧床病人不便于独自向医护人…

.NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试

2023年11月15日&#xff0c;对.net的开发圈是一个重大的日子&#xff0c;.net 8.0正式版发布。 圈内已经预热了有半个月有余&#xff0c;性能不断超越&#xff0c;开发体验越来越完美&#xff0c;早在.net 5.0的时候就各种吹风Aot编译&#xff0c;直到6.0 7.0使用仍然比较麻烦…

比一比国内的现货白银交易所

上海黄金交易所(SGE)是国内最大的白银交易平台之一。它作为中国金融期货市场的重要组成部分&#xff0c;能够提供完善的交易机制和规范的交易环境。SGE的交易品种丰富&#xff0c;包括现货白银&#xff0c;白银延期等的多种交易方式。SGE也具有较高的流动性和交易深度&#xff…

WireGuard 组网教程:快速构建安全高效的私密网络并实现内网穿透

文章目录 1 引言1.1 什么是WireGuard1.2 WireGuard可以用来做什么1.3 WireGuard原理1.4 WireGuard安装 2 WireGuard组网实现内网穿透2.1 前提条件2.2 网络拓扑结构2.3 具体步骤2.3.1 中继服务器配置2.3.2 其他peer2.3.3 测试 2.4 WireGuard配置文件说明 3 WireGuard工具3.1 wg-…

Numpy数组进阶_Python数据分析与可视化

Numpy数组进阶 Numpy的广播机制高级索引整数数组索引布尔索引花式索引 数组迭代 Numpy的广播机制 广播 (Broadcast) 是 numpy 对不同形状 (shape) 的数组&#xff0c;进行数值计算的方式。 对数组的算术运算通常在相应的元素上进行&#xff0c;当运算中的 2 个数组的形状不同时…

轻松实现文件改名:让新文件名与目录名称一致

在日常工作中&#xff0c;我们经常需要处理文件改名的问题。有时候&#xff0c;我们需要将新文件名设置为与目录名称相同&#xff0c;以方便管理和查找。然而&#xff0c;这个过程可能很繁琐&#xff0c;尤其是当你有大量的文件需要改名时。幸运的是&#xff0c;现在有一种简单…

Windows2019部署IIS后,浏览文件变成下载的问题解决过程

1、反复重起服务器、重建应用程序、建应用程序并改名才好使&#xff01; 2、总体上&#xff0c;角色为&#xff1a; 一、在Windows2019服务器上&#xff0c;填加角色&#xff0c;一路下一步&#xff0c;到填加服务器角色时&#xff0c;勾选了【Web服务器(IIS)】 二、然后在【…

阿里云2核2G3M服务器e实例、40G ESSD Entry系统盘性能测评

阿里云99元服务器新老用户同享2核2G经济型e实例、3M固定带宽和40G ESSD Entry系统盘&#xff0c;老用户也可以买&#xff0c;续费不涨价依旧是99元一年&#xff0c;阿里云百科aliyunbaike.com分享阿里云3M带宽服务器40G ESSD Entry云盘性能说明&#xff1a; 阿里云99元服务器配…

el-table操作栏添加el-dropdown获取当前行的数据

0 效果 点击子合同获取到当前行的id 1 代码 beforeHandleCommand(row, childCommand) {return { row: row, childCommand: childCommand }; }, addChildBtn(command) {const row command.row;if (command.childCommand 0) {// todo} else {// todo} },

el-select下拉框只回显value不回显label的原因以及解决方法

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 原因分析&#xff1a; 提示&#xff1a;这里填写问题的分析&#xff1a; el-select的采用的是map的key value结构&#xff0c;因此只显示value而不显示label的原因是&#xff0c;value的类型不正确&…
最新文章