功能实现——通过阿里云 OSS 实现文件管理

参考:
阿里云 OSS 注册与使用
阿里云 OSS 官方文档

1.需求分析

目前需要在 Spring Boot 项目中通过阿里云 OSS 来实现文件管理,例如文件上传、下载等操作。

阿里云 OSS 指的是阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的一种高可用、高扩展性的云端存储服务。它允许用户通过网络随时存储和访问大量数据,适用于各种场景,如网站托管、大数据分析、备份与归档等。

2.阿里云 OSS 开通与配置

2.1.登录阿里云官网

进入阿里云官网页面并登录(未注册需要先点击页面右上角进行注册):

在这里插入图片描述

2.2.搜索 OSS 服务并开通

(1)在页面上方的搜索框中搜索“OSS”并点击进入:

在这里插入图片描述

(2)第一次使用的账户可以免费试用(下面以免费试用为例):

在这里插入图片描述

在这里插入图片描述

(3)免费试用的页面如下(相比于付费的,少了自定义配置的过程):

在这里插入图片描述

付费版的配置如下,大家可以根据自己的实际需求进行配置。

在这里插入图片描述

(4)提交成功后,点击管理试用可以查看详细信息。

在这里插入图片描述

2.3.OSS 配置

(1)申请试用(或者付费开通之后)回到之前的页面点击管理控制台:

在这里插入图片描述

(2)点击"Bucket 列表"进入页面:

在这里插入图片描述

(3)创建 Bucket:

在这里插入图片描述

根据自己的需求选择相应配置:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

查看 Bucket 中的文件:

在这里插入图片描述

初始时为空,即没有任何文件:

在这里插入图片描述

需要注意的是,上述配置只是为了满足本文的简单需求,因此在配置时并未开启其它功能(例如实时日志查询、定时备份等)。大家在配置时需要根据实际情况来选择。

3.在项目使用阿里云 OSS

更加详细的信息见阿里云 OSS 官方文档

在这里插入图片描述

3.1.项目环境搭建

(1)在 IDEA 中创建一个 Spring Boot 项目,具体可以参考【环境搭建】使用IDEA创建SpringBoot项目详细步骤这篇文章。

(2)pom.xml 中添加如下依赖:

<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.17.4</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>

(3)application.yml 中的配置如下:

aliyun:
  OSS:
    endpoint: https://oss-cn-beijing.aliyuncs.com	# 上面创建 Bucket 过程中出现的域名
    accessKeyId: *******************			    # 用于标识用户的密钥 ID,类似于用户名,获取方式见下面的介绍
    accessKeySecret: ******************  			# 用于对请求进行加密签名的密钥,类似于密码,获取方式见下面的介绍
    objectName: myDir/								# 存储空间中的文件存放地址,其中的 myDir 是我手动创建的空目录
    bucketName: test-oos-bucket						# bucket 名称

accessKeyIdaccessKeySecret 的获取方式:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通过手机验证或者人脸验证后即可得到 accessKeyIdaccessKeySecret

在这里插入图片描述

3.2.代码实现

下面在 OSSUtil.java 工具类中封装相关方法:

package com.example.oss.util;

import com.aliyun.oss.OSS;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import com.aliyun.oss.model.PutObjectResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;

@Component
public class OSSUtil {
	//读取配置文件中的相关信息
    @Value("${aliyun.OSS.endpoint}")
    private String endpoint;

    @Value("${aliyun.OSS.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.OSS.accessKeySecret}")
    private String accessKeySecret;

    @Value("${aliyun.OSS.objectName}")
    private String objectName;

    @Value("${aliyun.OSS.bucketName}")
    private String bucketName;

    //具体文件操作见下面的代码
}

3.2.1.将本地文件上传到阿里云 OSS

/**
 * @description 将本地文件上传到阿里云 OSS
 * @param filePath 本地的文件路径
 * @return 文件下载地址
 */
public String uploadLocalFile(String filePath) {
    //创建 OSSClient 实例
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    File file = new File(filePath);
    // Object 完整路径,完整路径中不能包含 Bucket 名称,例如 myDir/output.pdf
    String objectNamePath = objectName + file.getName();
    try {
        //创建 PutObjectRequest 对象
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectNamePath, file);

        //如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
        // ObjectMetadata metadata = new ObjectMetadata();
        // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
        // metadata.setObjectAcl(CannedAccessControlList.Private);
        // putObjectRequest.setMetadata(metadata);

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

        //设置 URL 过期时间为 1 小时
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        //生成文件的下载 URL
        String url = ossClient.generatePresignedUrl(bucketName, objectNamePath, expiration).toString();
        return url;
    } catch (OSSException oe) {
        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());
    } catch (ClientException 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();
        }
    }
    return "null";
}

3.2.2.将前端传入的文件上传到阿里云 OSS

/**
 * @description 将前端传入所接收的文件上传到阿里云 OSS
 * @param file 前端传入所接收的文件
 * @param fileName 文件命名
 * @return 文件存储在云端的地址
 * @throws Exception
 */
public String uploadFrontFile(MultipartFile file, String fileName) throws Exception {
	// myDir\output2.pdf
    String objectNamePath = objectName + fileName;
    //创建 OSSClient 实例
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    try {
        //创建 PutObjectRequest 对象
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectNamePath, file.getInputStream());
        //设置该属性可以返回 response,如果不设置,则返回的 response 为空
        putObjectRequest.setProcess("true");
        //创建 PutObject 请求
        PutObjectResult result = ossClient.putObject(putObjectRequest);

        //设置 URL 过期时间为 1 小时
        Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
        //生成文件的下载 URL
        String url = ossClient.generatePresignedUrl(bucketName, objectNamePath, expiration).toString();
        return url;
    } catch (OSSException oe) {
        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());
    } catch (ClientException 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();
        }
    }
    return "null";
}

3.2.3.下载文件到本地

/**
 * @description 将前端传入所接收的文件上传到阿里云 OSS
 * @param fileName 要下载的阿里云 OSS 上的文件名
 * @param downFilePath 下载到本地的完整路径
 * @return 下载成功标识
 */
public String downloadFileToLocal(String fileName, String downFilePath) {
	// myDir\output.pdf
    String objectNamePath = objectName + fileName;
    //创建 OSSClient 实例
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    try {
        /**
         * 下载 Object 到本地文件,并保存到指定的本地路径中:
         * 1.如果指定的本地文件存在会覆盖,不存在则新建。
         * 2.如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。
         * */
        ossClient.getObject(new GetObjectRequest(bucketName, objectNamePath), new File(downFilePath));
    } catch (OSSException oe) {
        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());
    } catch (ClientException 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();
        }
    }
    return "success";
}

2.3.4.流式下载

/**
 * @description 流式下载文件
 * @param fileName 要流式下载的文件名称
 * @return 文件内容
 */
public String streamingDownload(String fileName) {
    // myDir\test.txt
    String objectNamePath = objectName + fileName;
    //创建 OSSClient 实例
    OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    StringBuilder builder = new StringBuilder();
    try {
        // ossObject 包含文件所在的存储空间名称、文件名称、文件元数据以及一个输入流
        OSSObject ossObject = ossClient.getObject(bucketName, objectNamePath);

        //读取文件内容
        System.out.println("Object content:");
        BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
        while (true) {
            String line = reader.readLine();
            if (line == null) {
                break;
            }
            System.out.println("\n" + line);
            builder.append("\n").append(line);
        }
        //数据读取完成后,获取的流必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作
        reader.close();
        //ossObject 对象使用完毕后必须关闭,否则会造成连接泄漏,导致请求无连接可用,程序无法正常工作
        ossObject.close();
    } catch (OSSException oe) {
        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());
    } 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();
        }
    }
    return builder.toString();
}

3.2.4.OSSController.java

package com.example.oss.controller;

import com.example.oss.util.OSSUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/oss")
public class OSSController {

    @Autowired
    private OSSUtil ossUtil;

    //上传本地文件
    @PostMapping("/uploadLocalFile")
    public String uploadLocalFile() {
        String filePath = "output.pdf";
        return ossUtil.uploadLocalFile(filePath);
    }

    //上传前端文件
    @PostMapping("/uploadFrontFile")
    public String uploadFrontFile(MultipartFile file) throws Exception {
        String fileName = "output2.pdf";
        return ossUtil.uploadFrontFile(file, fileName);
    }

    //下载文件到本地
    @PostMapping("/downloadFileToLocal")
    public String downloadFileToLocal() {
        String fileName = "output.pdf";
        String downFilePath = "D:\\output.pdf";
        return ossUtil.downloadFileToLocal(fileName, downFilePath);
    }

    //流式下载
    @PostMapping("/streamingDownload")
    public String streamingDownload() {
        String fileName = "test.txt";
        return ossUtil.streamingDownload(fileName);
    }
}

3.3.测试

3.3.1.上传本地文件到阿里云 OSS

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求),并返回了该文件的下载 URL:

http://localhost:8080/oss/uploadLocalFile

在这里插入图片描述

此时在 OSS 控制台可以发现已经成功上传了文件 output.pdf,它在目录 myDir/ 下:

在这里插入图片描述

3.3.2.前端上传文件到阿里云 OSS

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求),注意设置请求头中的 Content-Typemultipart/form-data

http://localhost:8080/oss/uploadFrontFile

在这里插入图片描述

在这里插入图片描述

此时在 OSS 控制台可以发现已经成功上传了文件 output2.pdf,它也在目录 myDir/ 下:

在这里插入图片描述

3.3.3.下载文件到本地

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求):

http://localhost:8080/oss/downloadFileToLocal

在这里插入图片描述

此时本地的 D 盘中也有了刚才下载的文件:

在这里插入图片描述

3.3.4.流式下载

考虑到 PDF 文件的读取内容可能为乱码,因此我先上传 test.txt 文件到阿里云 OSS ,其内容如下:

hello world!
你好!

在这里插入图片描述

启动项目后,在 Postman 中进行接口测试(注意是 POST 请求):

http://localhost:8080/oss/streamingDownload

在这里插入图片描述

### 本地前端对接阿里云OSS跨域设置 为了使本地前端能够成功对接阿里云OSS并处理跨域请求,需确保浏览器发送的预检请求(OPTIONS)得到正确响应。这涉及到调整OSS服务端的CORS配置以及可能需要修改Nginx或其他反向代理服务器的相关设置。 #### 修改OSS CORS规则 通过阿里云控制台为指定Bucket添加或编辑CORS规则来允许来自特定源的访问: - 登录[阿里云官网](https://www.aliyun.com/); - 进入对象存储OSS产品详情页找到对应的bucket名称; - 在权限管理下的`跨域设置(CORS)`选项卡内新增一条或多条策略声明[^2]; 每条记录应至少包含如下字段: | 字段名 | 描述 | | --- | --- | | AllowedOrigin | 允许发起跨域请求的实际地址列表,支持通配符*表示任意站点均可访问| | AllowedMethod | 支持的方法GET/PUT/POST/DELETE等,默认全部开放可按需限定范围| | AllowedHeader | 可选参数,定义哪些HTTP头部可以被包含于实际请求中传输给资源服务器| 例如下面是一个较为宽松但实用性强的例子: ```json [ { "AllowedOrigin": ["http://localhost:8080"], "AllowedMethod": ["GET","PUT"], "AllowedHeader": ["Authorization"] } ] ``` 此配置意味着只接受来源于`http://localhost:8080`路径下发出的GET和PUT类型的请求,并且这些请求能携带Authorization头信息用于身份验证过程[^3]。 #### Nginx反向代理配置优化 如果应用部署环境中有使用Nginx作为前置网关,则还需要适当调整其配置文件以确保整个链路畅通无阻: ```nginx location / { proxy_pass http://your_oss_endpoint; # 添加必要的headers以便让浏览器知道该接口支持跨域资源共享 add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization'; # 对于preflight request(OPTIONS),直接返回204状态码而不转交给后端处理 if ($request_method = 'OPTIONS') { return 204; } } ``` 上述片段展示了如何在Nginx层面进一步增强对于跨域的支持力度,特别是针对那些非简单请求(pre-flight),即带有自定义Headers或是采用了除GET/POST之外其他动词的情况时所必需执行的操作[^1]。 完成以上两步之后,理论上就可以实现在本地环境中顺利地从前端提交数据至远端位于阿里云上的Object Storage Service了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值