使用Spring WebClient通过预签名URL传文件到AWS S3遇凭证格式错误
场景说明
使用响应式Spring框架,通过Spring WebClient实现将.csv文件通过预签名URL上传至AWS S3,运行环境为Windows平台下的Java 1.8,未在WebClient中设置任何请求头。
代码实现
WebClient Bean配置
@Bean public WebClient webClient(WebClient.Builder webClientBuilder) { ConnectionProvider provider = ConnectionProvider .builder("customFixedConnectionPool").maxConnections(maxConnections).pendingAcquireTimeout( Duration.ofMillis(acquireTimeout)).build(); HttpClient httpClient = HttpClient.create(provider).resolver(DefaultAddressResolverGroup.INSTANCE); return webClientBuilder .clientConnector(new ReactorClientHttpConnector(httpClient)) .build(); }
FileUploadClient实现
说明:以下代码中,BodyInserters.fromResource(resource)返回BodyInserter<Resource, ReactiveHttpOutputMessage>
@Component public class FileUploadClient implements FileUploadClientInterface { public FileUploadClient(WebClient webClient) { this.webClient = webClient.mutate() .build(); } private final WebClient webClient; @Override public Mono<FileUploadResponse> uploadFIle(String preSignedUrl, String filePath) { Resource resource = new PathResource(filePath); return webClient.put() .uri(preSignedUrl) .body(BodyInserters.fromResource(resource)) .exchangeToMono(response -> { if (response.statusCode().equals(HttpStatus.NO_CONTENT)) { fileUploadResponse.setCode(HttpStatus.NO_CONTENT.value()); fileUploadResponse.setMessage("FILE_UPLOAD_SUCCESS"); return Mono.just(fileUploadResponse); } else { return response.bodyToMono(String.class) .flatMap(responseBodyString -> { fileUploadResponse.setCode(response.statusCode().value()); fileUploadResponse.setMessage(responseBodyString); return Mono.just(fileUploadResponse); }); } }).onErrorResume(throwable -> { fileUploadResponse.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); fileUploadResponse.setMessage(throwable.getMessage()); System.out.println("uploadLog Error: "+ throwable.getMessage()); return Mono.just(fileUploadResponse); }); } }
错误信息
执行时出现如下错误:
<?xml version="1.0" encoding="UTF-8"?> <Error><Code>AuthorizationQueryParametersError</Code><Message>Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting "<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request".</Message><RequestId>xxxxxx</RequestId><HostId>xxxxxx</HostId></Error>
预签名URL示例
使用的预签名URL格式如下:
https://xxx-xx-xxx-xx.s3.ap-southeast-1.amazonaws.com/PG00009912/PENDING/REQ_PG00009912_11112022.csv?X-Amz-Security-Token=dzEIz%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDLAplr5Vvi%2F0mZqIMSLABMetINeSCoOWcMxBPkBQN44hB1gyNBFfSPoAOLe0FY8lhBgsBis%2F7oRp3WU8H6qDUpI%2BsxCZEIFMWTkoP%2FIZP%2FAUG5ib8qAIx3gCuVtFLLx77kxdclqm8PhC6OlkYZ5fBrlUl7hHvY9w8hdFAWvRByaqsNKGWiiwwhaMvWZNI65O%2BoWZ84ESwBQCZxbp3e4TdWvUEuB%2FnVpyoMbUfugGxVK2rG58rgJpUKK5E9rv8ZTzIkUqjH7Ql5ZL%2FCt0%2FTa%2Fbj98617MKMk2bPu7dcLZpurNJ75J4AIH2a5%2Bql5tk7FB2f8uwIad3ZskiV5rtz%2FmyoqxFxc5eaaC6YbXDC06eHcMirg81U15XXqFgM2Qi%2FFjx3yutm0E59aPHSEz1K0ucfe2%2FUYLYbB57V%2FgFD8MO8pwHKmLDC0hXVFhXny8nnpPq5PBkqzZomk%2BBOiwBnkP5H98tRXRshwhqnmIMh80xyEy6%2FfCLBRSmJEMrzTpQqDpi%2B1%2Fg7wnOK8Gpv2iyLIU%2FBTD1%2FwgQ716qVsDjH8eTyPga513G6uZ94Kp4pKr1M1E1wsh2IbuvfMlPQ%2FOnL7zA07jZtNxWe%2B538Gs5%2FMwtz9fM%2FoEkeNolx96k3cgDCyQCiD8HU%2FKJk8UYUttSv8CBwkI3PwAW1Sdty0O2L0dGGyFFwwtu6wql8%2BJYxkAZOdZCxDDg2YoaeayVrjYq9mp9yZ%2BxJR1OsVuwmfRkQZcgkUPj7MnCGdgKN1KJAiNk6uVs%2FpSRxEWtSeJlhU806%2FACCiznbObBjIrEwg1WuV%2BkReFAoD86rDec%2BE%2BKmhjb2ahFqHqtY9V4YAYF82RtEzUQld6MQ%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20221110T101307Z&X-Amz-SignedHeaders=host&X-Amz-Expires=26212&X-Amz-Credential=H2DP6FQ%2F20221110%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Signature=787c80356d749c9e0e1231ceb75d64df77f1cf40830146dfa
解决方案
问题原因是WebClient对预签名URL进行了不必要的二次编码,将代码中的.uri(preSignedUrl)改为.uri(URI.create(preSignedUrl))即可解决。
内容的提问来源于stack exchange,提问作者techmagister
相关产品推荐
相关产品推荐

