Become a sponsor

云存储优势
如果还没有阿里云账号,需要先注册:
访问 阿里云官网。

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

注册完成后,登录阿里云控制台。
登录阿里云官网,在顶部搜索栏检索 对象存储 OSS。

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

按照提示完成 OSS 服务的开通。
温馨提示
从节省成本考虑,建议购买最低 100G 存储空间,余量不足时可以扩容,当然有条件也可以一次性购买大存储空间。
在 OSS 控制台页面,点击左侧菜单的 Bucket 列表。

点击左上角的 创建 Bucket。

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

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

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

点击 创建 AccessKey。

温馨提示
获取 AccessKey ID 和 AccessKey Secret,并妥善保存(注意:AccessKey Secret 只会显示一次)。
在 pom.xml 中添加阿里云 OSS 的 SDK 依赖:
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.17.2</version>
</dependency>在 xiaomayi-common/xiaomayi-oss 模块中已经引入此依赖,在实际使用时直接引入以下依赖即可:
<!-- 阿里云OSS云存储依赖模块 -->
<dependency>
    <groupId>com.xiaomayi</groupId>
    <artifactId>xiaomayi-oss</artifactId>
</dependency>在 application-aliyun.yml 中配置阿里云短信服务的参数:
# 阿里云
aliyun:
  # OSS云存储配置
  oss:
    bucketName: demo2
    endpoint: oss.example.com
    accessKeySecret: QLGvhLm0ZEYkGlLJMFxkbaISHOmgsN
    accessKeyId: LTAI4tMQVr1P2Mu6kXJ0pwgB创建 OSS 配置类
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;
    }
}在 xiaomayi-common/xiaomayi-oss 模块中,已集成 OSS云存储 工具类 AliyunOSSUtil 文件。
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;
    }
}以下是官方编写的创建文件上传、下载、删除等案例;
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 成功后输出结果:
{
    "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 按存储量、流量、请求次数等收费,使用前请了解相关计费规则。