First, I obtained the target file to be downloaded through Minio like this.
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 is extends FilterInputStream. So I thought of using the Response class to return a BufferedInputStream class to avoid load all bytes in memory.
@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()) ;
}
}
but i download a empty file with 0 bytes.
i can download full file with code like this: but use readAllBytes() method
@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()) ;
}
}
So, I changed my approach and tried to download a local file directly instead of the file stream returned through 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());
}
}
It works perfectly.
The first solution that came to my mind was to write the InputStream returned by HTTP to a local file, and then return the stream of the local file.
However, how can I directly return the InputStream to the front-end without using file transfer as an intermediary?
I try return Response.ok with inputstream,but it still not work.it still return 0 bytes file
@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();
}
}
Do something like this:
@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();
}
}
The reason is that you don't want to close the InputStream in the JAX-RS method