elunez/eladmin · error · BadRequestException
读取 S3 输入流时出错: {}
Error message
读取 S3 输入流时出错: {} What it means
BadRequestException passthrough from catch (IOException e) around IOUtils.toByteArray(s3InputStream) in privateDownload — thrown when reading the already-open S3 response stream fails mid-transfer. Unlike error 94 (service-level S3Exception), this is transport/stream level: connection reset while streaming, socket timeout, or truncated response while Base64-encoding a large file in memory.
Source
Thrown at eladmin-tools/src/main/java/me/zhengjie/service/impl/S3StorageServiceImpl.java:199
// 创建 GetObjectRequest,指定存储桶和文件键
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
.bucket(amzS3Config.getDefaultBucket())
.key(storage.getFilePath())
.build();
String base64Data;
// 使用 try-with-resources 确保流能被自动关闭
// s3Client.getObject() 返回一个 ResponseInputStream,它是一个包含S3对象数据的输入流
try (ResponseInputStream<GetObjectResponse> s3InputStream = s3Client.getObject(getObjectRequest)) {
// 使用 IOUtils.toByteArray 将输入流直接转换为字节数组
byte[] fileBytes = IOUtils.toByteArray(s3InputStream);
// 使用 Java 内置的 Base64 编码器将字节数组转换为 Base64 字符串
base64Data = Base64.getEncoder().encodeToString(fileBytes);
} catch (S3Exception e) {
// 处理 AWS 特定的异常
throw new BadRequestException("从 S3 下载文件时出错: " + e.awsErrorDetails().errorMessage());
} catch (IOException e) {
// 处理通用的 IO 异常 (IOUtils.toByteArray 可能会抛出)
throw new BadRequestException("读取 S3 输入流时出错: " + e.getMessage());
}
// 构造返回数据
Map<String, String> responseData = new HashMap<>();
// 文件名
responseData.put("fileName", storage.getFileName());
// 文件类型
responseData.put("fileMimeType", storage.getFileMimeType());
// 文件内容
responseData.put("base64Data", base64Data);
return responseData;
}
/**
* 检查云存储桶是否存在
* @param bucketName 存储桶名称
*/
@SuppressWarnings({"all"})
private boolean bucketExists(String bucketName) {View on GitHub (pinned to 55fbf70595)
Solutions
- Retry the download once — transient stream resets are common and a fresh getObject usually succeeds.
- For large files, stream to the response (or presigned URL) instead of byte[]+Base64 to avoid heap spikes and long single reads.
- Raise socket/attempt timeouts on the S3Client builder if reads time out on big objects.
- Check for proxies/LBs between the app and S3 with aggressive idle timeouts and bypass/raise them.
- Log the IOException message: 'Connection reset' vs 'heap space' point to network vs memory fixes.
Example fix
// before
byte[] fileBytes = IOUtils.toByteArray(s3InputStream);
base64Data = Base64.getEncoder().encodeToString(fileBytes);
} catch (IOException e) {
throw new BadRequestException("读取 S3 输入流时出错: " + e.getMessage());
}
// after: avoid full buffering — stream base64 directly to the caller
try (ResponseInputStream<GetObjectResponse> in = s3Client.getObject(getObjectRequest);
OutputStream out = response.getOutputStream()) {
Base64.getEncoder().wrap(out).transferTo? // JDK9+: use InputStream.transferTo
in.transferTo(Base64.getEncoder().wrap(out));
} catch (IOException e) {
log.error("Streaming S3 object failed, key={}", storage.getFilePath(), e);
throw new BadRequestException("读取 S3 输入流时出错: " + e.getMessage());
} Defensive patterns
Strategy: retry
Validate before calling
// fail fast on oversized objects before buffering to Base64
long maxBytes = 50L * 1024 * 1024;
HeadObjectResponse head = s3Client.headObject(HeadObjectRequest.builder()
.bucket(amzS3Config.getDefaultBucket()).key(storage.getFilePath()).build());
if (head.contentLength() > maxBytes) {
throw new BadRequestException("文件过大,请使用直链下载");
} Try / catch
int attempts = 0; while (true) { try { return privateDownload(id); } catch (BadRequestException e) { if (++attempts >= 2 || !e.getMessage().contains("输入流")) throw e; /* brief backoff, retry once for transient stream drop */ } } Prevention
- Stream large objects to the HTTP response instead of byte[] + Base64 to shorten single-read window.
- Tune S3Client socket/attempt timeouts for your largest object size.
- Watch for proxies with short idle timeouts between the app and S3.
When it happens
Trigger: Downloading a large S3 file where the HTTP stream drops mid-read (network reset, proxy idle timeout, S3 closing the connection), or the app/heap pressure causing stream read failures while loading the whole object into a byte[] for Base64.
Common situations: Big files (the whole object is buffered then Base64-encoded — roughly 1.33x size in heap); flaky networks / NAT idle timeouts on long reads; HTTP proxies between app and S3 cutting long responses; SDK socket timeout defaults too low for the object size.
Related errors
AI-assisted analysis of elunez/eladmin@55fbf70595 (2026-08-14).
Data as JSON: /api/errors/952a758c857efd03.
Report an issue: GitHub.