java 文件下载 io流操作

需要做一个下载功能 把项目临时文件里的压缩包 下载下来 想用浏览器下载 用了 response.getOutputStream(); 但是为啥下载下来的文件要么打不开 要么就是显示空文件无法下载 参考了站内很多代码都不行
我直接返回io流给前端的话 是可以下载的 但是为啥会重复调用接口
有没有解释下 或者给个可以实现 response.getOutputStream() 文件下载的demo 可有偿

    @POST
    @Path("/test")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public FileInputStream test(@Context HttpServletRequest request, @Context HttpServletResponse response) {
        FileInputStream fileInputStream = null;
        try {
            response.setContentType("application/zip");
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(文件名", "UTF-8"));
            fileInputStream = new FileInputStream(new File(文件地址));
            File file = new File(文件地址);
            file.delete();
            return fileInputStream;
        } catch (Exception e) {

            throw new RuntimeException("内部错误,请联系管理员!", e);
        }
//        finally {
//            try {
//                fileInputStream.close();
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }
//        }
        
    }

【以下回答由 GPT 生成】

首先,关于为什么直接将IO流返回给前端会重复调用接口的情况,主要是因为在使用response.getOutputStream()时,服务器在写入完毕后并没有关闭流,导致客户端一直在等待数据。当客户端接收到数据后,会认为下载完成,然后自动再次发送请求,导致重复调用接口。

解决这个问题的一种方式是,在控制器方法中将response.getOutputStream()方法返回的流内容写入到一个ByteArrayOutputStream中,然后再将ByteArrayOutputStream的内容作为字节数组返回给前端。这样可以确保流的数据被正确关闭,并且避免了重复调用接口的问题。

以下是示例代码的改进版:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;

@Path("/download")
public class DownloadController {

    @GET
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
    public Response downloadFile(@Context HttpServletRequest request) {
        String filePath = "文件地址";
        String fileName = "文件名";

        File file = new File(filePath);
        if (!file.exists()) {
            return Response.status(Response.Status.NOT_FOUND).entity("文件不存在").build();
        }

        try {
            FileInputStream fileInputStream = new FileInputStream(file);

            StreamingOutput streamingOutput = new StreamingOutput() {
                @Override
                public void write(OutputStream output) throws IOException {
                    try {
                        byte[] buffer = new byte[4096];
                        int bytesRead;

                        while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, bytesRead);
                        }
                    } finally {
                        fileInputStream.close();
                        output.close();
                    }
                }
            };

            return Response.ok(streamingOutput, MediaType.APPLICATION_OCTET_STREAM)
                    .header("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"))
                    .header("Content-Length", file.length())
                    .build();
        } catch (IOException e) {
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("服务器错误").build();
        }
    }
}

在此代码片段中,我们使用StreamingOutput将文件流写入到输出流中,并在结束后关闭文件流和输出流。同时,我们返回一个Response对象,并设置了文件名和文件大小。这样浏览器就可以正确下载文件,而不会重复调用接口。

请注意,您需要替换代码中的"文件地址"和"文件名"为实际的路径和文件名。

希望这个解决方案对您有帮助!如果还有其他问题,请随时提问。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

            InputStream data = XXXXXXX;   //接输入流
            int n;
            buf = new BufferedInputStream(data);
            out = response.getOutputStream();
            while ((n=buf.read())!=-1){
                out.write(n);
            }out.flush();