Skip to content

Commit 1588178

Browse files
committed
feat(s3): Add support for path-style access in S3 configuration
1 parent 0eeb64e commit 1588178

File tree

5 files changed

+47
-9
lines changed

5 files changed

+47
-9
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This addon is particularly useful for:
2222

2323
- Store BlueMap data in any S3-compatible storage service
2424
- Configure custom endpoints for self-hosted S3 solutions
25+
- Support for path-style access for compatibility with various S3 implementations (like MinIO)
2526
- Seamless integration with BlueMap's storage system
2627

2728
## Requirements
@@ -77,6 +78,10 @@ endpoint-url: "http://localhost:9000"
7778
# Optional: The root path in the S3 bucket where BlueMap data will be stored
7879
# Default is "." (root of the bucket)
7980
root-path: "."
81+
82+
# Optional: Force path style access for S3 (needed for MinIO)
83+
# Default is false (use virtual-hosted style)
84+
force-path-style: true
8085
```
8186

8287
### Configuration Options
@@ -91,6 +96,7 @@ root-path: "."
9196
| `secret-access-key` | The AWS secret access key for authentication | `bluemap-secret` |
9297
| `endpoint-url` | Optional: The endpoint URL for S3-compatible services (leave empty for AWS S3) | `http://localhost:9000` |
9398
| `root-path` | Optional: The root path in the S3 bucket where BlueMap data will be stored | `.` |
99+
| `force-path-style` | Optional: Force path style access for S3 (needed for MinIO) | `false` |
94100

95101
## Usage Examples
96102

@@ -110,6 +116,7 @@ access-key-id: "AKIAIOSFODNN7EXAMPLE"
110116
secret-access-key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
111117
endpoint-url: ""
112118
root-path: "."
119+
force-path-style: false
113120
```
114121

115122
### MinIO
@@ -128,6 +135,7 @@ access-key-id: "minioadmin"
128135
secret-access-key: "minioadmin"
129136
endpoint-url: "http://minio-server:9000"
130137
root-path: "."
138+
force-path-style: true
131139
```
132140

133141
### DigitalOcean Spaces
@@ -146,6 +154,7 @@ access-key-id: "your-spaces-key"
146154
secret-access-key: "your-spaces-secret"
147155
endpoint-url: "https://nyc3.digitaloceanspaces.com"
148156
root-path: "."
157+
force-path-style: false
149158
```
150159

151160
## Building from Source

src/main/java/dev/themeinerlp/bluemap/s3/storage/S3Configuration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,6 @@ public sealed interface S3Configuration permits S3StorageConfiguration {
3131

3232
String getRootPath();
3333

34+
boolean forcePathStyle();
35+
3436
}

src/main/java/dev/themeinerlp/bluemap/s3/storage/S3FileSystemFactory.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
*/
1818
package dev.themeinerlp.bluemap.s3.storage;
1919

20+
import software.amazon.nio.spi.s3.S3FileSystemProvider;
21+
import software.amazon.nio.spi.s3.S3XFileSystemProvider;
22+
2023
import java.net.URI;
2124
import java.nio.file.FileSystem;
2225
import java.util.Objects;
23-
import software.amazon.nio.spi.s3.S3FileSystemProvider;
24-
import software.amazon.nio.spi.s3.S3XFileSystemProvider;
2526

2627
final class S3FileSystemFactory {
27-
2828
private static final String AWS_REGION_KEY = "aws.region";
2929
public static final String DEFAULT_AWS_REGION = "us-east-1";
3030

@@ -43,20 +43,28 @@ public static S3Fs build(S3Configuration cfg) {
4343
final boolean thirdParty = cfg.getEndpointUrl() != null && !cfg.getEndpointUrl().isBlank();
4444
try {
4545
final URI uri;
46+
System.setProperty(AWS_REGION_KEY, cfg.getRegion() != null ? cfg.getRegion() : DEFAULT_AWS_REGION);
47+
System.setProperty("aws.accessKeyId", cfg.getAccessKeyId());
48+
System.setProperty("aws.secretAccessKey", cfg.getSecretAccessKey());
4649
if (thirdParty) {
47-
PROVIDER = new S3XFileSystemProvider();
4850
var url = URI.create(cfg.getEndpointUrl());
49-
System.setProperty("s3.spi.endpoint-protocol", url.toString().startsWith("https") ? "https" : "http");
50-
System.setProperty(AWS_REGION_KEY, cfg.getRegion() != null ? cfg.getRegion() : DEFAULT_AWS_REGION);
51+
if (!url.toString().startsWith("https")) {
52+
System.setProperty("s3.spi.endpoint-protocol", "http");
53+
}
54+
if (cfg.forcePathStyle()) {
55+
System.setProperty("s3.spi.force-path-style", "true");
56+
}
57+
PROVIDER = new S3XFileSystemProvider();
5158
String userInfo = buildUserInfo(cfg);
5259
uri = new URI("s3x", userInfo, url.getHost(), url.getPort(), "/" + cfg.getBucketName(), null, null);
5360
} else {
5461
// AWS S3 – the provider uses the default region/credentials chain
5562
PROVIDER = new S3FileSystemProvider();
56-
System.setProperty(AWS_REGION_KEY, cfg.getRegion() != null ? cfg.getRegion() : DEFAULT_AWS_REGION);
57-
String userInfo = buildUserInfo(cfg);
58-
uri = new URI("s3", userInfo,"/" + cfg.getBucketName(), null, null);
63+
64+
uri = new URI("s3","/" + cfg.getBucketName(), null, null);
5965
}
66+
System.out.println("Using S3 FileSystem Provider: " + PROVIDER.getClass().getName());
67+
System.out.println("S3 URI: " + uri);
6068
FileSystem fs = PROVIDER.getFileSystem(uri);
6169
return new S3Fs(fs, uri);
6270
} catch (Exception e) {

src/main/java/dev/themeinerlp/bluemap/s3/storage/S3Storage.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ public void initialize() throws IOException {
5252
try {
5353
S3FileSystemFactory.S3Fs handle = S3FileSystemFactory.build(configuration);
5454
this.s3FileSystem = handle.fileSystem();
55+
if (!Files.exists(getRooPath())) {
56+
Files.createDirectories(getRooPath());
57+
}
58+
// Preload the map storages into the cache
59+
Files.list(getRooPath())
60+
.filter(Files::isDirectory)
61+
.forEach(path -> {
62+
String mapId = path.getFileName().toString();
63+
System.out.println("Loading map storage for: " + mapId);
64+
mapStorages.get(mapId); // This will create the FileMapStorage if it doesn't exist
65+
});
5566
} catch (Exception e) {
5667
throw new IOException("Failed to initialize S3 storage", e);
5768
}

src/main/java/dev/themeinerlp/bluemap/s3/storage/S3StorageConfiguration.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public final class S3StorageConfiguration extends StorageConfig implements S3Con
5252

5353
@Comment("The root path in the S3 bucket (default: empty, meaning the root of the bucket)")
5454
private String rootPath = ".";
55+
56+
@Comment("Force path style access for S3 (default: false, use virtual-hosted style)")
57+
private boolean forcePathStyle = false;
5558

5659
@Override
5760
public Storage createStorage() throws ConfigurationException {
@@ -112,4 +115,9 @@ public String getRootPath() {
112115
.filter(path -> !path.isEmpty())
113116
.orElse(".");
114117
}
118+
119+
@Override
120+
public boolean forcePathStyle() {
121+
return forcePathStyle;
122+
}
115123
}

0 commit comments

Comments
 (0)