Java-183 OSS 上传实战:Java 原生与 Spring Boot 集成

TL;DR

  • 场景:本地 Java 与 Spring Boot 通过阿里云 Java SDK 3.8.0 向北京地域 OSS 上传图片。
  • 结论:示例可用;为生产落地需补齐 HTTPS、RAM/STS、CORS、Content-Type、日志与依赖安全等。
  • 产出:Maven 片段、YAML、Config/Service/Controller 与 Postman 验证 + 版本矩阵与错误速查卡。

版本矩阵

组件/项 版本/取值 已验证 说明
JDK 8 与 Spring Boot 2.3.x 匹配。生产可评估升级到 11/17。
Spring Boot 2.3.2.RELEASE 示例可跑;生态老,建议中长期规划 LTS(2.7/3.x)。
阿里云 OSS SDK(Java) 3.8.0 代码使用 OSSClientBuilder/OSS API 可用。
Endpoint https://oss-cn-beijing 强制 HTTPS 避免明文传输。
认证方式 RAM 子账号 + STS 临时凭证 生产环境推荐方案。
Bucket 既有存量 跨地域会报 301/NoSuchBucket,需与 endpoint 一致。
对象名策略 UUID.后缀 建议按 yyyy/MM/dd/uuid.ext 分层,便于清理与 CDN 缓存。
Content-Type 显式设置正确 MIME 浏览器直链预览依赖 ObjectMetadata。
CORS 补充分配规则 前端直连需在控制台配置允许来源/方法/头。
上传尺寸 小文件直传 大文件需使用 UploadFile/分片接口与断点续传。

OSS 存储实战

XML

xml 复制代码
<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.5</version>
    </dependency>
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.8.0</version>
    </dependency>
</dependencies>

对应的内容如下所示:

代码实现

java 复制代码
package icu.wzk;

public class FileUpload {

    private static final String ENDPOINT = "http://oss-cn-beijing.aliyuncs.com";
    private static final String ACCESS_KEY_ID = "自己的值";
    private static final String ACCESS_KEY_SECRET = "自己的值";

    public static void main(String[] args) throws Exception {
        // 云账号AccessKey有所有API访问权限
        // 建议遵循阿里云安全最佳实践: 创建并使用RAM子账号进行API访问或日常运维
        OSS ossClient = new OSSClientBuilder().build(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        InputStream inputStream = new FileInputStream(new File("wzk.png"));
        // 这里要写 Bucket 的名称 很早几年就创建过 直接用了
        ossClient.putObject("kangkang2017", "wzk.png", inputStream);
        ossClient.shutdown();
        System.out.println("上传图片完毕");
    }

}

执行结果如下所示:

我们去OSS平台查看结果:

Spring Boot 整合

XML

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>icu.wzk</groupId>
    <artifactId>untitled</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
        </dependency>
    </dependencies>
</project>

application.yml

yaml 复制代码
server:
  port: 9999

aliyun:
  endpoint: http://oss-cn-beijing.aliyuncs.com
  accessKeyId: <your access key id>
  accessKeySecret: <your access key secret>
  bucketName: <your bucket name>
  urlPrefix: <your folder name>

结果如下所示:

AliyunConfig

java 复制代码
package icu.wzk.config;


import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.CredentialsProvider;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun")
public class AliyunConfig {

    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String bucketName;
    private String urlPrefix;

    @Bean
    public OSSClient ossClient(){
        CredentialsProvider credentialsProvider = new DefaultCredentialProvider(accessKeyId, accessKeySecret);
        ClientConfiguration clientConfiguration = new ClientConfiguration();
        return new OSSClient(endpoint, credentialsProvider, clientConfiguration);
    }
}

编写结果如下所示:

实体对象

java 复制代码
package icu.wzk.model;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UploadResult {
    private String uid;
    private String name;
    private String status;
    private String response;
}

对应的内容如下:

Service

java 复制代码
package icu.wzk.service.impl;

@Service
@RequiredArgsConstructor
public class UploadServiceImpl implements UploadService {

    private final AliyunConfig aliyunConfig;
    private final OSSClient ossClient;
    private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg", ".jpeg", ".gif", ".png"};

    @Override
    public UploadResult upload(MultipartFile file) {
        boolean isLegal = false;
        for (String type : IMAGE_TYPE) {
            if (StringUtils.endsWithIgnoreCase(file.getOriginalFilename(),
                    type)) {
                isLegal = true;
                break;
            }
        }
        UploadResult uploadResult = new UploadResult();
        if (!isLegal) {
            uploadResult.setStatus("error");
            return uploadResult;
        }

        String fileName = file.getOriginalFilename();
        String filePath = UUID.randomUUID() + "." + StringUtils.substringAfterLast(fileName, ".");
        try {
            ossClient.putObject(aliyunConfig.getBucketName(), filePath, new ByteArrayInputStream(file.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
            uploadResult.setStatus("error");
            return uploadResult;
        }
        uploadResult.setStatus("ok");
        uploadResult.setName(this.aliyunConfig.getUrlPrefix() + filePath);
        uploadResult.setUid(String.valueOf(System.currentTimeMillis()));
        return uploadResult;
    }
}

对应的内容如下:

Controller

java 复制代码
package icu.wzk.controller;

@RestController
@RequestMapping("/Upload")
@RequiredArgsConstructor
public class UploadController {

    private final UploadService uploadService;

    @RequestMapping("/upload")
    public UploadResult upload(MultipartFile file) {
        return uploadService.upload(file);
    }

}

对应的内容如下:

启动类

java 复制代码
package icu.wzk;

@SpringBootApplication
public class StartApp {
    public static void main(String[] args) {
        SpringApplication.run(StartApp.class, args);
    }
}

编写结果如下所示:

测试效果

上传结束:

错误速查

症状 根因 定位 修复
403 SignatureDoesNotMatch / AccessDenied AK/SK 错、时间偏差、策略缺少 oss:PutObject 查看服务端时间、开启 SDK 日志、检查 RAM 策略 同步 NTP;为子账号授予最小权限(对目标 Bucket 的 PutObject/ListBucket);优先用 STS 临时凭证
404 NoSuchBucket / 301 重定向 Bucket 地域与 endpoint 不一致 OSS 控制台查看 Bucket 所在地域 统一使用与 Bucket 一致的 oss-.aliyuncs.com
直链下载不预览(图片变下载) 未设置 Content-Type 控制台/SDK 获取对象元信息 PutObject 时设置 ObjectMetadata#setContentType("image/png") 等正确 MIME
前端跨域失败(CORS 报错) 未配置 CORS 或规则过严 浏览器控制台预检失败、OSS 控制台无规则 在 OSS 控制台添加允许来源、GET/PUT、自定义头;最小化放行
UnknownHostException oss-cn-beijing.aliyuncs.com DNS/网络代理异常、endpoint 拼写错误 ping/nslookup/公司代理策略 修正域名;走 HTTPS;必要时直连公网或配置企业代理白名单
NoSuchMethodError/NoClassDefFoundError 依赖冲突(旧 OSSClient/日志桥接) mvn dependency:tree 统一使用 OSS + OSSClientBuilder;移除 log4j 1.x,使用 slf4j + logback
RequestTimeTooSkewed 服务器时间与 UTC 偏差大 服务端系统时间 开启 chrony/ntpd,保证误差 < 5 分钟
中文/空格文件名访问 404 未进行 URL 编码 访问日志 404,路径含空格/中文 统一进行 URLEncoder 或仅使用 ASCII 对象名
大文件/不稳定网络频繁失败 直传无断点续传 上传时长异常、偶发失败 使用分片上传 + 断点续传(SDK UploadFile/MultipartUpload)并设置重试与超时
返回结果可用性弱 未返回 etag/url/contentType 客户端二次处理困难 在响应中附带完整可用信息,便于前端直接使用

其他系列

🚀 AI篇持续更新中(长期更新)

AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究 ,持续打造实用AI工具指南!
AI研究-132 Java 生态前沿 2025:Spring、Quarkus、GraalVM、CRaC 与云原生落地
🔗 AI模块直达链接

💻 Java篇持续更新中(长期更新)

Java-180 Java 接入 FastDFS:自编译客户端与 Maven/Spring Boot 实战

MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务已完结,Dubbo已完结,MySQL已完结,MongoDB已完结,Neo4j已完结,FastDFS 已完结,OSS正在更新... 深入浅出助你打牢基础!
🔗 Java模块直达链接

📊 大数据板块已完成多项干货更新(300篇):

包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
🔗 大数据模块直达链接

相关推荐
爱笑的源码基地35 分钟前
智慧工地云平台源码,采用Java+SpringCloud+UniApp+MySql技术,支持多端展示,具备集团级多级权限管理。
人工智能·后端·spring·spring cloud·源码·智慧工地·工地智能管理
ALex_zry35 分钟前
系统编程的基石:补码循环溢出与Rust变量绑定的深度探索
开发语言·后端·rust
杰克逊的日记37 分钟前
业务系统从 AWS 云迁到阿里云,迁移到阿里云
阿里云·云计算·aws
ALex_zry38 分钟前
Rust语言基础分析与C++对比:系统编程的现代演进
java·c++·rust
从零开始学习人工智能40 分钟前
告别存储困境:RustFS 如何用内存安全重塑分布式对象存储
分布式·安全
Molesidy40 分钟前
【QT】【C++】基于QT的多线程分别管理GUI和运算任务
开发语言·c++·qt
yenggd42 分钟前
samba服务配置原理
服务器·开发语言·php
你不是我我1 小时前
【Java 开发日记】阻塞队列有哪些?拒绝策略有哪些?
java·开发语言
lpruoyu1 小时前
HTTP+XML形式完成请求交互
java