You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何最小改动将Spring Boot文件上传下载迁移至Amazon S3

最小改动适配Spring Boot应用到Amazon S3存储桶

Got it, let's walk through how to adapt your existing file upload/download logic to Amazon S3 with minimal changes—we’ll keep your controllers, HTML/Android integrations completely intact, and only swap out the local file system code in your com.erp.serviceimpl classes.


1. Add AWS S3 Dependencies

First, add the AWS SDK for Java 2.x (the latest stable version) to your build file. If you’re using Maven, update your pom.xml:

<dependencies>
    <!-- AWS S3 SDK -->
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
        <version>2.25.0</version> <!-- Use the latest stable version -->
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>regions</artifactId>
        <version>2.25.0</version>
    </dependency>
</dependencies>

For Gradle, add this to build.gradle:

implementation 'software.amazon.awssdk:s3:2.25.0'
implementation 'software.amazon.awssdk:regions:2.25.0'

2. Configure AWS S3 Settings

Add these properties to your application.properties (or application.yml) to avoid hardcoding credentials and bucket details:

# S3 Bucket Configuration
aws.s3.bucket-name=your-bucket-name-here
aws.region=your-aws-region (e.g., us-east-1, eu-west-1)

# Optional: Use environment variables or IAM roles for production
# aws.access-key=${AWS_ACCESS_KEY}
# aws.secret-key=${AWS_SECRET_KEY}

Pro Tip: For production (EC2/EKS/ECS), don’t hardcode access keys—use an IAM role attached to your instance/service instead, and the SDK will automatically pick up credentials.


3. Configure S3 Client Bean

Create a configuration class to initialize the S3Client (Spring will manage this bean for you):

package com.erp.config; // Adjust package as needed

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration
public class AwsS3Config {
    @Value("${aws.region}")
    private String awsRegion;

    @Bean
    public S3Client s3Client() {
        return S3Client.builder()
                .region(Region.of(awsRegion))
                // Uncomment below for local testing with LocalStack
                // .endpointOverride(URI.create("http://localhost:4566"))
                .build();
    }
}

4. Modify Your Service Implementation Class

Now, update your com.erp.serviceimpl class to replace local file system operations with S3 API calls. Here’s how to handle both upload scenarios:

Step 4.1: Inject Dependencies

First, inject the S3Client and bucket name into your service class:

package com.erp.serviceimpl;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Base64;

@Service
public class YourFileServiceImpl { // Replace with your actual service class name
    private final S3Client s3Client;
    private final String bucketName;

    // Constructor injection (preferred over @Autowired)
    public YourFileServiceImpl(S3Client s3Client, @Value("${aws.s3.bucket-name}") String bucketName) {
        this.s3Client = s3Client;
        this.bucketName = bucketName;
    }

    // Rest of your methods go below...
}

Step 4.2: Replace Multipart File Upload Logic

Swap your local file writing code with S3 upload:

// Old local file code (remove this)
// File destFile = new File(uploadDir + "/" + fileName);
// file.transferTo(destFile);

// New S3 upload code
public void uploadMultipartFile(MultipartFile file, String fileName) throws IOException {
    try {
        PutObjectRequest uploadRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key("uploads/" + fileName) // Use prefix like "uploads/" to simulate folders
                .contentType(file.getContentType())
                .build();

        s3Client.putObject(uploadRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
    } catch (S3Exception e) {
        throw new RuntimeException("Failed to upload file to S3", e);
    }
}

Step 4.3: Replace Base64 File Upload Logic

Adapt your Android Base64-to-file code for S3:

// Old local file code (remove this)
// byte[] fileBytes = Base64.getDecoder().decode(base64Data);
// File destFile = new File(uploadDir + "/" + fileName);
// Files.write(destFile.toPath(), fileBytes);

// New S3 upload code for Base64 data
public void uploadBase64File(String base64Data, String fileName) {
    try {
        byte[] fileBytes = Base64.getDecoder().decode(base64Data);
        PutObjectRequest uploadRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key("uploads/" + fileName)
                .build();

        s3Client.putObject(uploadRequest, RequestBody.fromBytes(fileBytes));
    } catch (S3Exception e) {
        throw new RuntimeException("Failed to upload Base64 file to S3", e);
    }
}

Step 4.4: Replace File Download Logic

Swap local file reading with S3 download:

// Old local file code (remove this)
// File file = new File(downloadDir + "/" + fileName);
// return Files.readAllBytes(file.toPath());

// New S3 download code (returns byte array, matching your existing method signature)
public byte[] downloadFile(String fileName) {
    try {
        GetObjectRequest downloadRequest = GetObjectRequest.builder()
                .bucket(bucketName)
                .key("uploads/" + fileName) // Match the prefix used during upload
                .build();

        return s3Client.getObjectAsBytes(downloadRequest).asByteArray();
    } catch (S3Exception e) {
        if (e.statusCode() == 404) {
            throw new RuntimeException("File not found in S3", e);
        }
        throw new RuntimeException("Failed to download file from S3", e);
    }
}

5. Key Notes for Minimal Impact

  • No Controller Changes: Your existing HTML/Android endpoints (receiving MultipartFile or Base64) will work exactly as before—we’re only changing the service layer implementation.
  • Simulate Folders: S3 doesn’t have real folders, but using key prefixes like uploads/invoices/ will organize files like a folder structure in the S3 console.
  • Permissions: If you need public access to files, add .acl(ObjectCannedACL.PUBLIC_READ) to your PutObjectRequest (or configure bucket policies for finer control).

内容的提问来源于stack exchange,提问作者nilesh soni

火山引擎 最新活动