提问人:commonBoy 提问时间:9/14/2023 最后编辑:commonBoy 更新时间:9/19/2023 访问量:81
使用 quarkus 直接返回 InputStream 时如何避免 oom
how to avoid oom when direct return InputStream using quarkus
问:
首先,我像这样获得了要通过 Minio 下载的目标文件。
public GetObjectResponse getFile(String bucket, String object) {
validateEnableMinio();
try {
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(bucket)
.object(object)
.build();
return minioClient.getObject(getObjectArgs);
} catch (Exception e) {
LOGGER.error("minio exception", e);
throw new RuntimeException("get file error");
}
}
GetObjectResponse 是 FilterInputStream 的扩展。因此,我想到了使用 Response 类返回一个 BufferedInputStream 类,以避免加载内存中的所有字节。
@GET
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Uni<Response> download1(@RestQuery String fileName) {
var stat = minioService.statObject(fileName);
try (GetObjectResponse getObjectResponse = minioService.getFile(fileName)) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(getObjectResponse);
Response.ResponseBuilder builder = Response.ok(bufferedInputStream);
builder.type(MediaType.APPLICATION_OCTET_STREAM_TYPE);
builder.header("Content-Disposition", "attachment; filename=" + stat.object());
return Uni.createFrom().item(builder.build());
} catch (Exception e) {
LOGGER.error("error", e);
return Uni.createFrom().item(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()) ;
}
}
但是我下载了一个 0 字节的空文件。
我可以下载带有如下代码的完整文件: 但是使用 readAllBytes() 方法
@GET
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Uni<Response> download1(@RestQuery String fileName) {
var stat = minioService.statObject(fileName);
try (GetObjectResponse getObjectResponse = minioService.getFile(fileName)) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(getObjectResponse);
Response.ResponseBuilder builder = Response.ok(bufferedInputStream.readAllBytes());
builder.type(MediaType.APPLICATION_OCTET_STREAM_TYPE);
builder.header("Content-Disposition", "attachment; filename=" + stat.object());
return Uni.createFrom().item(builder.build());
} catch (Exception e) {
LOGGER.error("error", e);
return Uni.createFrom().item(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()) ;
}
}
因此,我改变了方法,尝试直接下载本地文件,而不是通过HTTP返回的文件流。
@GET
@Path("/download1")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Uni<Response> downloadFile() {
String filePath = "D:\\xxx\\settings.xml"; // 替换为实际文件路径
File file = new File(filePath);
if (!file.exists() || !file.isFile()) {
return Uni.createFrom().item(Response.status(Response.Status.NOT_FOUND).build());
}
try {
InputStream is = new BufferedInputStream(new FileInputStream(file));
return Uni.createFrom().item(Response.ok(is)
.header("Content-Disposition", "attachment; filename=" + file.getName())
.build());
} catch (IOException e) {
return Uni.createFrom().item(Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("error").build());
}
}
它工作得很好。
我想到的第一个解决方案是将 HTTP 返回的 InputStream 写入本地文件,然后返回本地文件的流。
但是,如何在不使用文件传输作为中介的情况下直接将 InputStream 返回到前端?
后续行动
我尝试使用输入流返回 Response.ok,但它仍然没有 work.it 仍然返回 0 字节文件
@GET
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download1(@RestQuery String fileName) {
var stat = minioService.statObject(fileName);
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket("test1")
.object(fileName)
.build();
try (InputStream inputStream = minioClient.getObject(getObjectArgs)) {
Response.ResponseBuilder builder = Response.ok(inputStream);
builder.type(MediaType.APPLICATION_OCTET_STREAM_TYPE);
builder.header("Content-Disposition", "attachment; filename=" + stat.object());
return builder.build();
} catch (Exception e) {
LOGGER.error("exception", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
答:
0赞
geoand
9/19/2023
#1
执行如下操作:
@GET
@Path("/download")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response download1(@RestQuery String fileName, @Context Closer closer) {
var stat = minioService.statObject(fileName);
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket("test1")
.object(fileName)
.build();
InputStream inputStream;
try {
inputStream = minioClient.getObject(getObjectArgs);
closer.add(inputStream);
Response.ResponseBuilder builder = Response.ok(inputStream);
builder.type(MediaType.APPLICATION_OCTET_STREAM_TYPE);
builder.header("Content-Disposition", "attachment; filename=" + stat.object());
return builder.build();
} catch (Exception e) {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception ignored) {}
}
LOGGER.error("exception", e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
原因是您不想在 JAX-RS 方法中关闭 InputStream
评论
Uni<Response>
Response
RestResponse
InputStream
.ok()