0

I have a problem about fixing 401 Unauthorized issue when I upload a file through Cloudflare R2 Storage in my Spring Boot example.

I get Token value, access key, secret key and jurisdiction-specific endpoints for S3 clients from Account API Tokens of R2 Storage.

Bucket name is stored in Eastern Europe (EEUR)

Here are the dependencies defined in pom.xml:

    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
        <version>${cloudflare-aws-sdk-version}</version>
        <scope>compile</scope>
    </dependency>

    <!-- AWS SDK’s Apache HTTP-client integration -->
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>apache-client</artifactId>
        <version>${cloudflare-aws-sdk-version}</version>
    </dependency>

    <!-- force the httpclient version to 4.5.13 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>${apache-httpclient.version}</version>
    </dependency>

Here is the application.yml file:

cloudflare:
  r2:
    endpoint: https://<account-id>.r2.cloudflarestorage.com
    accessKey: access-key
    secretKey: secret-key
    bucket: buckey-name

Here is the CloudflareProperties class:

@Getter
@Setter
@ConfigurationProperties(prefix = "cloudflare.r2")
public class CloudflareProperties {
    private String endpoint;
    private String accessKey;
    private String secretKey;
}

This is the CloudflareR2Config class:

@Configuration
@EnableConfigurationProperties(CloudflareProperties.class)
@RequiredArgsConstructor
public class CloudflareR2Config {

    private final CloudflareProperties cloudflareProperties;

    @Bean
    public S3Client s3Client() {

        return S3Client.builder()
                .httpClientBuilder(ApacheHttpClient.builder())
                .region(Region.of("eeur"))
                .endpointOverride(URI.create(cloudflareProperties.getEndpoint()))
                .credentialsProvider(StaticCredentialsProvider.create(
                        AwsBasicCredentials.create(
                                cloudflareProperties.getAccessKey(),
                                cloudflareProperties.getSecretKey())))
                .serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build())
                .build();
    }

}

And this is the service class:

@Service
@RequiredArgsConstructor
public class MediaService {

    private final S3Client s3Client;

    @Value("${cloudflare.r2.bucket}")
    private String bucket;

    public String uploadFile(MultipartFile file) {

        String originalFilename = Optional.ofNullable(file.getOriginalFilename())
                .orElseThrow(() -> new UnsupportedMediaTypeException("Filename is missing"))
                .toLowerCase();

        String contentType = Optional.ofNullable(file.getContentType())
                .orElseThrow(() -> new UnsupportedMediaTypeException("Content-Type is unknown"));

        String folder = switch (getFileExtension(originalFilename)) {
            case "jpg", "jpeg", "png" -> "images";
            case "mp4", "mov" -> "videos";
            default -> throw new UnsupportedMediaTypeException("Unsupported file type: " + contentType);
        };

        String key = folder + "/" + UUID.randomUUID() + "-" + originalFilename;

        PutObjectRequest putRequest = PutObjectRequest.builder()
                .bucket(bucket)
                .key(key)
                .contentType(contentType)
                .build();

        try {
            s3Client.putObject(putRequest, RequestBody.fromBytes(file.getBytes()));
        } catch (IOException e) {
            throw new FileUploadException("File upload to Cloudflare R2 failed", e);
        }

        return key;
    }


    private String getFileExtension(String filename) {
        int lastDot = filename.lastIndexOf('.');
        if (lastDot == -1 || lastDot == filename.length() - 1) {
            throw new IllegalArgumentException("Invalid file extension in filename: " + filename);
        }
        return filename.substring(lastDot + 1);
    }
}

This is the controller class:

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/media")
public class MediaController {

    private final MediaService mediaService;

    @PostMapping("/upload")
    public ResponseEntity<String> uploadImage(@RequestParam("file") MultipartFile file) {

        String key = mediaService.uploadFile(file);
        return ResponseEntity.ok(key);
    }
}

When I send a request to localhost:7777/api/media/upload, I got this error:

{
    "error": "Internal Server Error",
    "message": "Unauthorized (Service: S3, Status Code: 401, Request ID: null) (SDK Attempt Count: 1)"
}

How can I fix the issue?

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.