• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

weimin96 / oss-spring-boot-starter / #5

06 Apr 2026 02:14PM UTC coverage: 42.506% (-21.1%) from 63.625%
#5

push

github

web-flow
Merge pull request #9 from weimin96/spring3

Spring3

52 of 509 new or added lines in 8 files covered. (10.22%)

536 of 1261 relevant lines covered (42.51%)

0.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.95
/oss-spring-boot3-starter/src/main/java/com/wiblog/oss/service/BucketOperations.java
1
package com.wiblog.oss.service;
2

3
import com.wiblog.oss.bean.OssProperties;
4
import lombok.extern.slf4j.Slf4j;
5
import software.amazon.awssdk.services.s3.S3AsyncClient;
6
import software.amazon.awssdk.services.s3.model.*;
7
import software.amazon.awssdk.transfer.s3.S3TransferManager;
8

9
import java.util.List;
10

11
/**
12
 * Bucket 级别操作。
13
 *
14
 * <p>覆盖 AWS S3 SDK v2 中常用的 Bucket 管理能力:</p>
15
 * <ul>
16
 *   <li>版本控制(Versioning)</li>
17
 *   <li>生命周期规则(Lifecycle)</li>
18
 *   <li>CORS 配置</li>
19
 *   <li>存储加密配置(SSE)</li>
20
 *   <li>访问控制策略(Bucket Policy)</li>
21
 *   <li>Public Access Block(阻止公共访问)</li>
22
 * </ul>
23
 *
24
 * @author panwm
25
 */
26
@Slf4j
1✔
27
public class BucketOperations extends Operations {
28

29
    public BucketOperations(OssProperties ossProperties, S3AsyncClient client,
30
                            S3TransferManager transferManager) {
31
        super(ossProperties, client, transferManager);
1✔
32
    }
1✔
33

34
    // ----------------------------------------------------------------
35
    // 版本控制
36
    // ----------------------------------------------------------------
37

38
    /**
39
     * 启用当前 Bucket 的版本控制。
40
     */
41
    public void enableVersioning() {
NEW
42
        enableVersioning(ossProperties.getBucketName());
×
NEW
43
    }
×
44

45
    public void enableVersioning(String bucketName) {
NEW
46
        setVersioningStatus(bucketName, BucketVersioningStatus.ENABLED);
×
NEW
47
    }
×
48

49
    /**
50
     * 挂起(暂停)版本控制(已有版本保留,新上传不产生版本)。
51
     */
52
    public void suspendVersioning() {
NEW
53
        suspendVersioning(ossProperties.getBucketName());
×
NEW
54
    }
×
55

56
    public void suspendVersioning(String bucketName) {
NEW
57
        setVersioningStatus(bucketName, BucketVersioningStatus.SUSPENDED);
×
NEW
58
    }
×
59

60
    /**
61
     * 获取 Bucket 版本控制状态。
62
     *
63
     * @return "Enabled" / "Suspended" / null(未开启)
64
     */
65
    public String getVersioningStatus() {
NEW
66
        return getVersioningStatus(ossProperties.getBucketName());
×
67
    }
68

69
    public String getVersioningStatus(String bucketName) {
NEW
70
        GetBucketVersioningResponse resp = handleRequest(() ->
×
NEW
71
                client.getBucketVersioning(GetBucketVersioningRequest.builder().bucket(bucketName).build()));
×
NEW
72
        if (resp == null || resp.status() == null) {
×
NEW
73
            return null;
×
74
        }
NEW
75
        return resp.status().toString();
×
76
    }
77

78
    // ----------------------------------------------------------------
79
    // 生命周期规则
80
    // ----------------------------------------------------------------
81

82
    /**
83
     * 设置 Bucket 生命周期规则(覆盖所有已有规则)。
84
     *
85
     * @param rules 生命周期规则列表
86
     */
87
    public void putLifecycleRules(List<LifecycleRule> rules) {
NEW
88
        putLifecycleRules(ossProperties.getBucketName(), rules);
×
NEW
89
    }
×
90

91
    public void putLifecycleRules(String bucketName, List<LifecycleRule> rules) {
NEW
92
        PutBucketLifecycleConfigurationRequest req = PutBucketLifecycleConfigurationRequest.builder()
×
NEW
93
                .bucket(bucketName)
×
NEW
94
                .lifecycleConfiguration(BucketLifecycleConfiguration.builder().rules(rules).build())
×
NEW
95
                .build();
×
NEW
96
        handleRequest(() -> client.putBucketLifecycleConfiguration(req));
×
NEW
97
        log.info("Set {} lifecycle rules on bucket [{}]", rules.size(), bucketName);
×
NEW
98
    }
×
99

100
    /**
101
     * 获取 Bucket 生命周期规则。
102
     *
103
     * @return 规则列表,若未配置则返回空列表
104
     */
105
    public List<LifecycleRule> getLifecycleRules() {
NEW
106
        return getLifecycleRules(ossProperties.getBucketName());
×
107
    }
108

109
    public List<LifecycleRule> getLifecycleRules(String bucketName) {
NEW
110
        GetBucketLifecycleConfigurationResponse resp = handleRequest(() ->
×
NEW
111
                client.getBucketLifecycleConfiguration(
×
NEW
112
                        GetBucketLifecycleConfigurationRequest.builder().bucket(bucketName).build()));
×
NEW
113
        if (resp == null) {
×
NEW
114
            return List.of();
×
115
        }
NEW
116
        return resp.rules();
×
117
    }
118

119
    /**
120
     * 删除 Bucket 所有生命周期规则。
121
     */
122
    public void deleteLifecycleRules() {
NEW
123
        deleteLifecycleRules(ossProperties.getBucketName());
×
NEW
124
    }
×
125

126
    public void deleteLifecycleRules(String bucketName) {
NEW
127
        handleRequest(() -> client.deleteBucketLifecycle(
×
NEW
128
                DeleteBucketLifecycleRequest.builder().bucket(bucketName).build()));
×
NEW
129
        log.info("Deleted lifecycle rules on bucket [{}]", bucketName);
×
NEW
130
    }
×
131

132
    /**
133
     * 便捷方法:添加一条"N天后删除过期文件"的生命周期规则。
134
     *
135
     * @param ruleId         规则 ID(全局唯一)
136
     * @param prefix         作用路径前缀,传 "" 表示全 Bucket
137
     * @param expirationDays 过期天数
138
     */
139
    public void addExpirationRule(String ruleId, String prefix, int expirationDays) {
NEW
140
        addExpirationRule(ossProperties.getBucketName(), ruleId, prefix, expirationDays);
×
NEW
141
    }
×
142

143
    public void addExpirationRule(String bucketName, String ruleId, String prefix, int expirationDays) {
NEW
144
        LifecycleRule rule = LifecycleRule.builder()
×
NEW
145
                .id(ruleId)
×
NEW
146
                .status(ExpirationStatus.ENABLED)
×
NEW
147
                .filter(LifecycleRuleFilter.builder()
×
NEW
148
                        .prefix(prefix)
×
NEW
149
                        .build())
×
NEW
150
                .expiration(LifecycleExpiration.builder()
×
NEW
151
                        .days(expirationDays)
×
NEW
152
                        .build())
×
NEW
153
                .build();
×
154

155
        // 合并已有规则
NEW
156
        List<LifecycleRule> existing = new java.util.ArrayList<>(getLifecycleRules(bucketName));
×
NEW
157
        existing.removeIf(r -> ruleId.equals(r.id())); // 替换同 ID 的规则
×
NEW
158
        existing.add(rule);
×
NEW
159
        putLifecycleRules(bucketName, existing);
×
NEW
160
        log.info("Added expiration rule [{}] on bucket [{}]: {} days, prefix='{}'",
×
NEW
161
                ruleId, bucketName, expirationDays, prefix);
×
NEW
162
    }
×
163

164
    // ----------------------------------------------------------------
165
    // CORS 配置
166
    // ----------------------------------------------------------------
167

168
    /**
169
     * 设置 Bucket CORS 规则。
170
     *
171
     * @param corsRules CORS 规则列表
172
     */
173
    public void putCorsRules(List<CORSRule> corsRules) {
NEW
174
        putCorsRules(ossProperties.getBucketName(), corsRules);
×
NEW
175
    }
×
176

177
    public void putCorsRules(String bucketName, List<CORSRule> corsRules) {
NEW
178
        PutBucketCorsRequest req = PutBucketCorsRequest.builder()
×
NEW
179
                .bucket(bucketName)
×
NEW
180
                .corsConfiguration(CORSConfiguration.builder().corsRules(corsRules).build())
×
NEW
181
                .build();
×
NEW
182
        handleRequest(() -> client.putBucketCors(req));
×
NEW
183
        log.info("Set CORS rules on bucket [{}]", bucketName);
×
NEW
184
    }
×
185

186
    /**
187
     * 获取 Bucket CORS 配置。
188
     */
189
    public List<CORSRule> getCorsRules() {
NEW
190
        return getCorsRules(ossProperties.getBucketName());
×
191
    }
192

193
    public List<CORSRule> getCorsRules(String bucketName) {
NEW
194
        GetBucketCorsResponse resp = handleRequest(() ->
×
NEW
195
                client.getBucketCors(GetBucketCorsRequest.builder().bucket(bucketName).build()));
×
NEW
196
        if (resp == null) {
×
NEW
197
            return List.of();
×
198
        }
NEW
199
        return resp.corsRules();
×
200
    }
201

202
    /**
203
     * 便捷方法:为前端直传场景配置宽松 CORS(允许所有来源)。
204
     */
205
    public void allowAllOriginsCors() {
NEW
206
        allowAllOriginsCors(ossProperties.getBucketName());
×
NEW
207
    }
×
208

209
    public void allowAllOriginsCors(String bucketName) {
NEW
210
        CORSRule rule = CORSRule.builder()
×
NEW
211
                .allowedOrigins("*")
×
NEW
212
                .allowedMethods("GET", "PUT", "POST", "DELETE", "HEAD")
×
NEW
213
                .allowedHeaders("*")
×
NEW
214
                .exposeHeaders("ETag", "x-amz-request-id")
×
NEW
215
                .maxAgeSeconds(3600)
×
NEW
216
                .build();
×
NEW
217
        putCorsRules(bucketName, List.of(rule));
×
NEW
218
    }
×
219

220
    /**
221
     * 删除 Bucket CORS 配置。
222
     */
223
    public void deleteCorsRules() {
NEW
224
        deleteCorsRules(ossProperties.getBucketName());
×
NEW
225
    }
×
226

227
    public void deleteCorsRules(String bucketName) {
NEW
228
        handleRequest(() -> client.deleteBucketCors(
×
NEW
229
                DeleteBucketCorsRequest.builder().bucket(bucketName).build()));
×
NEW
230
    }
×
231

232
    // ----------------------------------------------------------------
233
    // Bucket 策略(Policy)
234
    // ----------------------------------------------------------------
235

236
    /**
237
     * 获取 Bucket 访问策略 JSON 字符串。
238
     *
239
     * @return JSON 策略字符串,若未配置返回 null
240
     */
241
    public String getBucketPolicy() {
NEW
242
        return getBucketPolicy(ossProperties.getBucketName());
×
243
    }
244

245
    public String getBucketPolicy(String bucketName) {
NEW
246
        GetBucketPolicyResponse resp = handleRequest(() ->
×
NEW
247
                client.getBucketPolicy(GetBucketPolicyRequest.builder().bucket(bucketName).build()));
×
NEW
248
        return resp == null ? null : resp.policy();
×
249
    }
250

251
    /**
252
     * 设置 Bucket 访问策略。
253
     *
254
     * @param policyJson JSON 格式的 IAM 策略
255
     */
256
    public void putBucketPolicy(String policyJson) {
NEW
257
        putBucketPolicy(ossProperties.getBucketName(), policyJson);
×
NEW
258
    }
×
259

260
    public void putBucketPolicy(String bucketName, String policyJson) {
NEW
261
        handleRequest(() -> client.putBucketPolicy(
×
NEW
262
                PutBucketPolicyRequest.builder().bucket(bucketName).policy(policyJson).build()));
×
NEW
263
        log.info("Updated policy on bucket [{}]", bucketName);
×
NEW
264
    }
×
265

266
    /**
267
     * 删除 Bucket 访问策略。
268
     */
269
    public void deleteBucketPolicy() {
NEW
270
        deleteBucketPolicy(ossProperties.getBucketName());
×
NEW
271
    }
×
272

273
    public void deleteBucketPolicy(String bucketName) {
NEW
274
        handleRequest(() -> client.deleteBucketPolicy(
×
NEW
275
                DeleteBucketPolicyRequest.builder().bucket(bucketName).build()));
×
NEW
276
        log.info("Deleted policy on bucket [{}]", bucketName);
×
NEW
277
    }
×
278

279
    // ----------------------------------------------------------------
280
    // Public Access Block(公共访问屏蔽)
281
    // ----------------------------------------------------------------
282

283
    /**
284
     * 启用所有公共访问屏蔽(最高安全级别)。
285
     */
286
    public void blockAllPublicAccess() {
NEW
287
        blockAllPublicAccess(ossProperties.getBucketName());
×
NEW
288
    }
×
289

290
    public void blockAllPublicAccess(String bucketName) {
NEW
291
        PublicAccessBlockConfiguration config = PublicAccessBlockConfiguration.builder()
×
NEW
292
                .blockPublicAcls(true)
×
NEW
293
                .ignorePublicAcls(true)
×
NEW
294
                .blockPublicPolicy(true)
×
NEW
295
                .restrictPublicBuckets(true)
×
NEW
296
                .build();
×
NEW
297
        handleRequest(() -> client.putPublicAccessBlock(
×
NEW
298
                PutPublicAccessBlockRequest.builder()
×
NEW
299
                        .bucket(bucketName)
×
NEW
300
                        .publicAccessBlockConfiguration(config)
×
NEW
301
                        .build()));
×
NEW
302
        log.info("Blocked all public access on bucket [{}]", bucketName);
×
NEW
303
    }
×
304

305
    /**
306
     * 获取 Bucket 公共访问屏蔽配置。
307
     */
308
    public PublicAccessBlockConfiguration getPublicAccessBlock() {
NEW
309
        return getPublicAccessBlock(ossProperties.getBucketName());
×
310
    }
311

312
    public PublicAccessBlockConfiguration getPublicAccessBlock(String bucketName) {
NEW
313
        GetPublicAccessBlockResponse resp = handleRequest(() ->
×
NEW
314
                client.getPublicAccessBlock(
×
NEW
315
                        GetPublicAccessBlockRequest.builder().bucket(bucketName).build()));
×
NEW
316
        return resp == null ? null : resp.publicAccessBlockConfiguration();
×
317
    }
318

319
    // ----------------------------------------------------------------
320
    // 服务端加密(SSE)
321
    // ----------------------------------------------------------------
322

323
    /**
324
     * 为 Bucket 启用 AES-256 服务端加密(SSE-S3)。
325
     */
326
    public void enableServerSideEncryption() {
NEW
327
        enableServerSideEncryption(ossProperties.getBucketName());
×
NEW
328
    }
×
329

330
    public void enableServerSideEncryption(String bucketName) {
NEW
331
        ServerSideEncryptionRule rule = ServerSideEncryptionRule.builder()
×
NEW
332
                .applyServerSideEncryptionByDefault(
×
NEW
333
                        ServerSideEncryptionByDefault.builder()
×
NEW
334
                                .sseAlgorithm(ServerSideEncryption.AES256)
×
NEW
335
                                .build())
×
NEW
336
                .bucketKeyEnabled(true)  // 减少 KMS 调用成本
×
NEW
337
                .build();
×
338

NEW
339
        handleRequest(() -> client.putBucketEncryption(
×
NEW
340
                PutBucketEncryptionRequest.builder()
×
NEW
341
                        .bucket(bucketName)
×
NEW
342
                        .serverSideEncryptionConfiguration(
×
NEW
343
                                ServerSideEncryptionConfiguration.builder().rules(rule).build())
×
NEW
344
                        .build()));
×
NEW
345
        log.info("Enabled SSE-S3 encryption on bucket [{}]", bucketName);
×
NEW
346
    }
×
347

348
    /**
349
     * 获取 Bucket 加密配置。
350
     */
351
    public ServerSideEncryptionConfiguration getEncryptionConfiguration() {
NEW
352
        return getEncryptionConfiguration(ossProperties.getBucketName());
×
353
    }
354

355
    public ServerSideEncryptionConfiguration getEncryptionConfiguration(String bucketName) {
NEW
356
        GetBucketEncryptionResponse resp = handleRequest(() ->
×
NEW
357
                client.getBucketEncryption(
×
NEW
358
                        GetBucketEncryptionRequest.builder().bucket(bucketName).build()));
×
NEW
359
        return resp == null ? null : resp.serverSideEncryptionConfiguration();
×
360
    }
361

362
    // ----------------------------------------------------------------
363
    // 私有工具
364
    // ----------------------------------------------------------------
365

366
    private void setVersioningStatus(String bucketName, BucketVersioningStatus status) {
NEW
367
        PutBucketVersioningRequest req = PutBucketVersioningRequest.builder()
×
NEW
368
                .bucket(bucketName)
×
NEW
369
                .versioningConfiguration(VersioningConfiguration.builder().status(status).build())
×
NEW
370
                .build();
×
NEW
371
        handleRequest(() -> client.putBucketVersioning(req));
×
NEW
372
        log.info("Set versioning status [{}] on bucket [{}]", status, bucketName);
×
NEW
373
    }
×
374
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc