为什么我总是收到“无法重试请求,因为无法重置请求流”?

Why do I keep getting 'Request cannot be retried, because the request stream could not be reset.'?

提问人:TheStranger 提问时间:9/25/2023 最后编辑:TheStranger 更新时间:9/25/2023 访问量:27

问:

我有这个问题,在过去的两周里我一直被困住了。我有这个代码,它基本上接受一个包含文件的 HttpServletRequest,并将其上传到 S3:

private UploadResultData uploadPackageAndCalculateMD5(HttpServletRequest request, String bucket, String uploadUrl) {
    try {
        // Create an InputStream from the request
        InputStream fileInputStream = request.getInputStream();

        // Create a DigestInputStream to calculate MD5 hash
        MessageDigest md = MessageDigest.getInstance("MD5");
        DigestInputStream digestInputStream = new DigestInputStream(inputStream, md);

        // Create a CountingInputStream to calculate file size
        CountingInputStream countingStream = new CountingInputStream(digestInputStream);

        // Create an UploadRequest to upload the file to S3
        UploadRequest uploadRequest = UploadRequest.builder()
                .putObjectRequest(b -> b.bucket(bucket).key(uploadUrl))
                .requestBody(AsyncRequestBody.fromInputStream(countingStream, request.getContentLengthLong(), s3Pool))
                .addTransferListener(new TransferListener() {
                    double lastKnownProgress = 0;

                    @Override
                    public void bytesTransferred(Context.BytesTransferred context) {
                        TransferProgressSnapshot snapshot = context.progressSnapshot();
                        OptionalDouble optionalDouble = snapshot.ratioTransferred();

                        if (optionalDouble.isPresent()) {
                            double currentProgress = optionalDouble.getAsDouble();

                            if (Math.abs(currentProgress - lastKnownProgress) >= 0.01) { // Update every 1%
                                lastKnownProgress = currentProgress;

                                // Create a Map to store progress data
                                Map<String, Object> progressData = new HashMap<>();
                                progressData.put("transfer", humanReadableByteCountSI(snapshot.transferredBytes()));
                                progressData.put("total", humanReadableByteCountSI(snapshot.totalBytes().getAsLong()));
                                progressData.put("ratio", optionalDouble.getAsDouble());

                                UploadController.this.updateProgress(uploadUrl, progressData); // use key to update specific progress
                            }
                        }
                    }
                })
                .build();

        // Upload the file to S3
        Upload upload = s3TransferManager.upload(uploadRequest);

        // Wait for the upload to complete
        PutObjectResponse response = upload.completionFuture().join().response();

        // Calculate MD5 hash and file size
        byte[] digest = md.digest();
        String md5Hash = DatatypeConverter.printHexBinary(digest).toLowerCase();
        System.out.println("MD5 hash: " + md5Hash);
        long fileSize = countingStream.getByteCount();
        System.out.println("File size: " + fileSize);

        // Check if the md5 hash was calculated
        if (md5Hash.isEmpty()) {
            log.error("MD5 hash calculation failed");
            return null;
        }

        // Check if the file size was calculated
        if (fileSize == 0) {
            log.error("File size calculation failed");
            return null;
        }

        System.out.println("File Upload Done!");

        // Create an UploadResultData object to return
        UploadResultData uploadResultData = new UploadResultData();
        uploadResultData.setMd5Hash(md5Hash);
        uploadResultData.setFileSize(fileSize);

        return uploadResultData;
    } catch (Exception e) {
        // Log the error and return null to indicate failure
        log.error("Failed to upload package: " + e.getMessage());
        e.printStackTrace();
        return null;
    }
}

当我上传超过 5 GB 的文件时,或者如果客户端取消上传请求,我会收到以下错误:

Request cannot be retried, because the request stream could not be reset.

这是我的班级:AwsConfig

@Configuration
public class AwsConfig {

    private static final Region awsRegion = Region.US_EAST_1;
    @Value("************")
    private String accessKey;
    @Value("************")
    private String secretKey;
    @Value("************")
    private String endpoint;

    @Bean
    public S3Client s3Client() {
        if (endpoint == null || endpoint.isEmpty()) throw new RuntimeException("needs s3 endpoint");
        var s3Url = endpoint.replaceAll("/$", ""); // remove trailing slash

        AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);

        return S3Client.builder()
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .endpointOverride(URI.create(s3Url))
                .region(awsRegion)
                .build();
    }

    @Bean
    public S3AsyncClient s3AsyncClient() {
        AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);

        return S3AsyncClient.builder()
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .endpointOverride(URI.create(endpoint))
                .region(awsRegion)
                .build();
    }

    @Bean
    public S3TransferManager asyncTransferManager(S3AsyncClient s3AsyncClient) {
        return S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
    }
}

是什么导致了错误,我该如何修复它?

spring-boot amazon-s3 aws-sdk 输入流 awss3transfermanager

评论


答: 暂无答案