Skip to content

云存储优势

  1. 高可用性:阿里云OSS提供99.999999999%的数据持久性和99.99%的服务可用性。
  2. 弹性扩展:根据业务需求自动扩展存储容量,无需担心存储瓶颈。
  3. 安全性:支持数据加密、访问控制、日志记录等安全功能。
  4. 成本效益:按需付费,降低存储成本。
  5. 易集成:提供丰富的SDK和API。

注册阿里云账号

如果还没有阿里云账号,需要先注册:

访问 阿里云官网

点击右上角的 免费注册,按照提示填写信息,完成账号注册。

注册完成后,登录阿里云控制台。

开通 OSS 服务

登录阿里云官网,在顶部搜索栏检索 对象存储 OSS

进入 OSS 控制台页面,点击 立即购买

按照提示完成 OSS 服务的开通。

温馨提示

从节省成本考虑,建议购买最低 100G 存储空间,余量不足时可以扩容,当然有条件也可以一次性购买大存储空间。

创建 OSS Bucket

OSS 控制台页面,点击左侧菜单的 Bucket 列表。

点击左上角的 创建 Bucket

填写 Bucket 信息:

Bucket 名称:自定义一个全局唯一的名称(如 my-springboot-bucket)。
地域:选择离你最近的区域(如 华东1(杭州))。
存储类型:选择标准存储(默认)。
读写权限:选择 私有(推荐)或 公共读(根据需求选择)。

点击 完成创建,完成 Bucket 创建。

点击 进入Bucket 可以查阅桶信息。

获取 AccessKey

登录阿里云控制台,鼠标悬停在右上角头像上,选择 AccessKey 管理。在 阿里云控制台 创建 AccessKey(AccessKey ID 和 AccessKey Secret)。

点击 创建 AccessKey

温馨提示

获取 AccessKey IDAccessKey Secret,并妥善保存(注意:AccessKey Secret 只会显示一次)。

添加OSS存储依赖

pom.xml 中添加阿里云 OSSSDK 依赖:

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

xiaomayi-common/xiaomayi-oss 模块中已经引入此依赖,在实际使用时直接引入以下依赖即可:

js
<!-- 阿里云OSS云存储依赖模块 -->
<dependency>
    <groupId>com.xiaomayi</groupId>
    <artifactId>xiaomayi-oss</artifactId>
</dependency>

配置OSS存储服务

application-aliyun.yml 中配置阿里云短信服务的参数:

js
# 阿里云
aliyun:
  # OSS云存储配置
  oss:
    bucketName: demo2
    endpoint: oss.example.com
    accessKeySecret: QLGvhLm0ZEYkGlLJMFxkbaISHOmgsN
    accessKeyId: LTAI4tMQVr1P2Mu6kXJ0pwgB

创建 OSS 配置类

js
package com.xiaomayi.oss.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * <p>
 * 阿里云OSS云存储配置类
 * </p>
 *
 * @author 小蚂蚁云团队
 * @since 2024-05-30
 */
@Configuration
// 指定配置文件位置
@ConfigurationProperties(prefix = "aliyun.oss")
public class AliyunOssConfig {

    /**
     * 存储桶名称
     */
    private static String bucketName;

    /**
     * 地域节点
     */
    private static String endpoint;

    /**
     * 安全认证ID
     */
    private static String accessKeyId;

    /**
     * 安全认证秘钥
     */
    private static String accessKeySecret;

    public static String getBucketName() {
        return bucketName;
    }

    public void setBucketName(String bucketName) {
        AliyunOssConfig.bucketName = bucketName;
    }

    public static String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        AliyunOssConfig.endpoint = endpoint;
    }

    public static String getAccessKeyId() {
        return accessKeyId;
    }

    public void setAccessKeyId(String accessKeyId) {
        AliyunOssConfig.accessKeyId = accessKeyId;
    }

    public static String getAccessKeySecret() {
        return accessKeySecret;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        AliyunOssConfig.accessKeySecret = accessKeySecret;
    }
}

创建OSS工具类

xiaomayi-common/xiaomayi-oss 模块中,已集成 OSS云存储 工具类 AliyunOSSUtil 文件。

js
package com.xiaomayi.oss.utils;


import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.ObjectMetadata;
import com.xiaomayi.core.utils.DateUtils;
import com.xiaomayi.core.utils.StringUtils;
import com.xiaomayi.oss.config.AliyunOssConfig;
import com.xiaomayi.oss.vo.AliyunOSSVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;

/**
 * <p>
 * 阿里云OSS云存储工具类
 * </p>
 *
 * @author 小蚂蚁云团队
 * @since 2024-05-30
 */
@Slf4j
@Component
public class AliyunOSSUtil {

    /**
     * 允许文件上传后缀
     */
    private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg", ".jpeg", ".gif", ".png"};

    /**
     * 创建OSS连接实例
     *
     * @return 返回结果
     */
    private static OSS getOssClient() {
        // 获取oss的地域节点
        String endpoint = AliyunOssConfig.getEndpoint();
        // 获取oss的AccessKeySecret
        String accessKeySecret = AliyunOssConfig.getAccessKeySecret();
        // 获取oss的AccessKeyId
        String accessKeyId = AliyunOssConfig.getAccessKeyId();

        ClientBuilderConfiguration conf = new ClientBuilderConfiguration();
        // 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
        conf.setMaxConnections(200);
        // 设置Socket层传输数据的超时时间,默认为50000毫秒。
        conf.setSocketTimeout(10000);
        // 设置建立连接的超时时间,默认为50000毫秒。
        conf.setConnectionTimeout(10000);
        // 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
        conf.setConnectionRequestTimeout(1000);
        // 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
        conf.setIdleConnectionTime(10000);
        // 设置失败请求重试次数,默认为3次。
        conf.setMaxErrorRetry(5);
        // 设置是否支持将自定义域名作为Endpoint,默认支持。
        conf.setSupportCname(true);
        // 设置是否开启二级域名的访问方式,默认不开启。
        // conf.setSLDEnabled(true);
        // 设置连接OSS所使用的协议(HTTP或HTTPS),默认为HTTP。
        conf.setProtocol(Protocol.HTTPS);
        // 设置用户代理,指HTTP的User-Agent头,默认为aliyun-sdk-java。
        /*conf.setUserAgent("aliyun-sdk-java");
        // 设置代理服务器端口。
        conf.setProxyHost("<yourProxyHost>");
        // 设置代理服务器验证的用户名。
        conf.setProxyUsername("<yourProxyUserName>");
        // 设置代理服务器验证的密码。
        conf.setProxyPassword("<yourProxyPassword>");*/
        // 设置是否开启HTTP重定向,默认开启。
        conf.setRedirectEnable(true);
        // 设置是否开启SSL证书校验,默认开启。
        conf.setVerifySSLEnable(true);
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, conf);
        return ossClient;
    }

    /**
     * 根据文件路径获取OSS访问地址
     * 备注:需要对中文路径进行URL编码
     *
     * @param filePath 文件路径
     * @return 返回结果
     */
    public static String getFileURL(String filePath) {
        // 地域节点
        String endpoint = AliyunOssConfig.getEndpoint();
        // 判断文件路径是否已包含
        if (filePath.contains(endpoint)) {
            // 直接返回
            return filePath;
        }
        String encode = null;
        try {
            // 文件路径分裂处理
            String[] strings = filePath.split("/");
            // 获取文件路径和文件名
            String lastIndex = strings[strings.length - 1];
            // URL编码
            lastIndex = URLEncoder.encode(lastIndex, "utf-8");
            // 重新赋值
            strings[strings.length - 1] = lastIndex;
            // 数组重新已斜杠拼接
            encode = String.join("/", strings);
        } catch (UnsupportedEncodingException e) {
            log.error("文件处理错误:{}", e.getMessage());
        }
        // 返回结果
        return String.format("%s://%s/%s", Protocol.HTTPS, AliyunOssConfig.getEndpoint(), encode);
    }

    /**
     * 上传文件
     *
     * @param uploadFile 文件对象
     * @param folder     文件夹
     * @param fileName   文件名
     * @return 返回结果
     */
    public static AliyunOSSVO upload(MultipartFile uploadFile, String folder, String fileName) {
        // 获取OSS存储桶名称
        String bucketName = AliyunOssConfig.getBucketName();
        // 获取文件原名称
        String originalFilename = uploadFile.getOriginalFilename();
        // 构建日期路径, 例如:OSS目标文件夹/2024/05/30/文件名
        // 文件上传的路径地址
        String filePath = folder + "/" + fileName;
        // 获取文件输入流
        InputStream inputStream = null;
        try {
            inputStream = uploadFile.getInputStream();
        } catch (IOException e) {
            log.error("上传文件异常:{}", e.getMessage());
        }
        /*
         * 下面两行代码是重点坑:
         * 现在阿里云OSS 默认图片上传ContentType是image/jpeg
         * 也就是说,获取图片链接后,图片是下载链接,而并非在线浏览链接,
         * 因此,这里在上传的时候要解决ContentType的问题,将其改为image/jpg
         */
        ObjectMetadata metadata = new ObjectMetadata();
        for (String type : IMAGE_TYPE) {
            if (StringUtils.endsWithIgnoreCase(uploadFile.getOriginalFilename(), type)) {
                metadata.setContentType("image/jpg");
            }
        }
        // 获取阿里云OSS实例
        OSS ossClient = getOssClient();
        // 文件上传至阿里云OSS
        ossClient.putObject(bucketName, filePath, inputStream, metadata);

        // 获取文件上传后的图片返回地址
        String fileUrl = getFileURL(filePath);

        // 实例化VO对象
        AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
        aliyunOSSVO.setFileName(originalFilename);
        aliyunOSSVO.setFilePath(filePath);
        aliyunOSSVO.setFileUrl(fileUrl);

        // 返回结果
        return aliyunOSSVO;
    }

    /**
     * 上传文件至OSS
     *
     * @param filePath 文件完整路径
     * @param folder   文件夹
     * @param delFile  是否删除
     * @return 返回结果
     */
    public static AliyunOSSVO uploadWithOss(String filePath, String folder, Boolean delFile) {
        // 实例化文件
        File file = new File(filePath);
        // 判断文件是否存在
        if (!file.exists()) {
            return null;
        }
        // 原始文件名
        String fileName = file.getName();
        // 获取OSS存储桶名称
        String bucketName = AliyunOssConfig.getBucketName();
        // 日期文件夹路径
        String datePath = DateUtils.datePath();
        // 准备上传OSS的文件路径和文件名
        String objectName = folder + "/" + datePath + "/" + fileName;
        // 实例化文件流
        FileInputStream fileInputStream = null;
        try {
            // 根据文件创建文件流
            fileInputStream = new FileInputStream(file);
            // 获取OSS云存储实例
            OSS ossClient = getOssClient();
            // 文件上传至阿里云OSS
            ossClient.putObject(bucketName, objectName, fileInputStream);
        } catch (FileNotFoundException e) {
            log.error("文件上传失败:{}", e.getMessage());
        } finally {
            try {
                if (null != fileInputStream) {
                    // 关闭文件流
                    fileInputStream.close();
                    // 删除文件
                    if (delFile) {
                        file.delete();
                    }
                }
            } catch (IOException e) {
                log.error("文件流关闭失败:{}", e.getMessage());
            }
        }

        // 获取文件上传后的地址
        String fileUrl = getFileURL(objectName);

        // 实例化VO对象
        AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
        aliyunOSSVO.setFileName(fileName);
        aliyunOSSVO.setFilePath(filePath);
        aliyunOSSVO.setFileUrl(fileUrl);

        // 返回结果
        return aliyunOSSVO;
    }

    /**
     * 上传文件流至OSS
     *
     * @param inputStream 文件流
     * @param folder      文件夹
     * @param fileName    文件名称
     * @return 返回结果
     */
    public static AliyunOSSVO uploadWithInputStream(InputStream inputStream, String folder, String fileName) {
        // 获取OSS存储桶名称
        String bucketName = AliyunOssConfig.getBucketName();
        // 新文件名称
        String filePath = String.format("%s/%s/%s", folder, DateUtils.datePath(), fileName);

        try {
            // 获取OSS存储实例
            OSS ossClient = getOssClient();
            // 文件上传至阿里云OSS
            ossClient.putObject(bucketName, filePath, inputStream);
        } catch (OSSException oe) {
            log.error("文件上传失败:{}", oe.getMessage());
        } finally {
            try {
                if (null != inputStream) {
                    // 关闭文件流
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error("文件流关闭失败:{}", e.getMessage());
            }
        }

        // 获取文件上传后的地址
        String fileUrl = getFileURL(filePath);

        // 实例化VO对象
        AliyunOSSVO aliyunOSSVO = new AliyunOSSVO();
        aliyunOSSVO.setFileName(fileName);
        aliyunOSSVO.setFilePath(filePath);
        aliyunOSSVO.setFileUrl(fileUrl);

        // 返回结果
        return aliyunOSSVO;
    }

    /**
     * 下载远程OSS文件至本地
     *
     * @param filePath OSS文件路径
     * @param newFile  目标文件
     */
    public static void writeLocalFile(String filePath, String newFile) {
        try {
            // 实例化目标文件
            File destFile = new File(newFile);
            // 创建目录
            if (destFile.getParentFile().exists()) {
                destFile.getParentFile().mkdirs();
            }
            // 实例化OSS客户端实例
            OSS ossClient = getOssClient();
            // 获取OSS云存储桶名称
            String bucketName = AliyunOssConfig.getBucketName();
            // 根据存储桶名称、文件路径获取OSS文件对象
            OSSObject ossObject = ossClient.getObject(bucketName, filePath);
            // 读取文件内容
            InputStream inputStream = ossObject.getObjectContent();
            if (null != inputStream) {
                // 把输入流放入缓存流
                BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
                // 实例化目标文件流
                FileOutputStream fileOutputStream = new FileOutputStream(newFile);
                // 把输出流放入缓存流
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = bufferedInputStream.read(buffer)) != -1) {
                    bufferedOutputStream.write(buffer, 0, len);
                }
                bufferedOutputStream.flush();
                bufferedOutputStream.close();
                bufferedInputStream.close();
            }
        } catch (Exception e) {
            log.error("文件写入本地失败:{}", e.getMessage());
        }
    }


    /**
     * 删除OSS文件
     *
     * @param filePath OSS文件路径
     * @return 返回结果
     */
    public static Boolean delete(String filePath) {
        try {
            // 获取OSS云存储实例
            OSS ossClient = getOssClient();
            // 获取OSS云存储桶名称
            String bucketName = AliyunOssConfig.getBucketName();
            // 从OSS存储桶中删除指定文件
            ossClient.deleteObject(bucketName, filePath);
            // 返回结果
            return true;
        } catch (Exception e) {
            log.error("删除文件失败:{}", e.getMessage());
            // 返回结果
            return false;
        }
    }

    /**
     * OSS云存储文件拷贝
     *
     * @param oldFilePath  原文件
     * @param destFilePath 目标文件
     * @return 返回结果
     */
    public static boolean copyFile(String oldFilePath, String destFilePath) {
        // 原文件判空
        if (StringUtils.isEmpty(oldFilePath)) {
            return false;
        }
        // 目标文件判空
        if (StringUtils.isEmpty(destFilePath)) {
            return false;
        }
        try {
            // 获取OSS云存储桶名称
            String bucketName = AliyunOssConfig.getBucketName();
            // 获取OSS云存储客户端实例
            OSS ossClient = getOssClient();
            // 判断存储桶中是否次存在原文件
            if (ossClient.doesObjectExist(bucketName, oldFilePath)) {
                // 从存储桶A拷贝文件A至存储桶B文件B
                ossClient.copyObject(bucketName, oldFilePath, bucketName, destFilePath);
            }
            return true;
        } catch (OSSException e) {
            log.error("文件拷贝失败:{}", e.getMessage());
        }
        return false;
    }

}

测试OSS云存储

以下是官方编写的创建文件上传、下载、删除等案例;

js
package com.xiaomayi.admin.controller.demo;

import com.xiaomayi.core.utils.R;
import com.xiaomayi.oss.utils.AliyunOSSUtil;
import com.xiaomayi.oss.vo.AliyunOSSVO;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

/**
 * <p>
 * 案例测试 前端控制器
 * </p>
 *
 * @author 小蚂蚁云团队
 * @since 2024-05-26
 */
@Slf4j
@RestController
@RequestMapping("/oss")
@AllArgsConstructor
public class OSSController {

    /**
     * 上传文件至OSS云存储
     *
     * @param file 文件对象
     * @return 返回结果
     */
    @PostMapping("/uploadFile")
    public R uploadFile(@RequestParam("file") MultipartFile file) {
        // 定义存储目录
        String folder = "test";
        // 定义新文件名
        String newFileName = file.getOriginalFilename();
        // 上传文件
        AliyunOSSVO aliyunOSSVO = AliyunOSSUtil.upload(file, folder, newFileName);
        // 返回结果
        return R.ok(aliyunOSSVO);
    }

    /**
     * 文件下载
     *
     * @return 返回结果
     */
    @GetMapping("/downloadFile")
    public R downloadFile() {
        // OSS文件地址
        String ossFilePath = "test/logo.jpg";
        // 本地文件地址
        String filePath = "E:/logo.jpg";
        // 读取OSS文件并写入本地文件
        AliyunOSSUtil.writeLocalFile(ossFilePath, filePath);
        return R.ok();
    }

    /**
     * 文件删除
     *
     * @return 返回结果
     */
    @DeleteMapping("/deleteFile")
    public R deleteFile() {
        AliyunOSSUtil.delete("test/logo.jpg");
        return R.ok();
    }

}

文件上传 OSS 成功后输出结果:

js
{
    "code": 0,
    "msg": "操作成功",
    "data": {
        "fileName": "logo.jpg",
        "filePath": "test/logo.jpg",
        "fileUrl": "https://oss-cn-shanghai.aliyuncs.com/test/logo.jpg"
    },
    "ok": true
}

前往阿里云OSS云存储对应的 xm-example 存储桶中查看,可以看到文件已上传成功,如下图:

温馨提示

本章节详细的阐述了后端服务是如何上传、下载、删除文件,实际使用时可以直接在前端应用中对接阿里云 OSS云存储,直接将上传成功后的文件地址提交后端服务保存即可,避免造成应用服务器压力过载。

总结

通过以上步骤,你可以成功将阿里云 OSS 集成到项目中,实现文件的上传、下载和删除功能。

注意事项

AccessKey 安全:不要将 AccessKey 直接暴露在前端代码中,确保后端安全存储。

Bucket 权限:根据业务需求设置 Bucket 的读写权限(私有、公共读、公共读写)。

文件大小限制:OSS 默认支持最大 5TB 的文件,但上传时需注意网络带宽和超时问题。

费用:OSS 按存储量、流量、请求次数等收费,使用前请了解相关计费规则。

小蚂蚁云团队 · 提供技术支持

小蚂蚁云 新品首发
新品首发,限时特惠,抢购从速! 全场95折
赋能开发者,助理企业发展,提供全方位数据中台解决方案。
获取官方授权