Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Dúvida com OutputStream e memória

Esse tipo de código geralmente usado para fazer download de arquivos em aplicações web e que usam um buffer para escrever a saída sempre me causam uma dúvida. A dúvida é em relação ao uso da memória pela JVM, supomos que devemos fazer download de um arquivo jpg de tamanho relativamente grande, com o código abaixo, como esse buffer trabalha com a memória? a cada iteração do while essa quantidade de bytes já é lida e jogada para a saída e logo após essa quantidade de memória é liberada? ou apenas após de ler todos os bytes da imagem(carrega a quantidade total de bytes na memória?) e dar o outputStream.close ou o flush esses bytes vão para a saida e a memória é liberada?

public class DownloadFileServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        String filePath = "C:/Test/Download/MYPIC.JPG";
        File downloadFile = new File(filePath);
        FileInputStream inStream = new FileInputStream(downloadFile);
                 String relativePath = getServletContext().getRealPath("");

        ServletContext context = getServletContext();

        String mimeType = context.getMimeType(filePath);
        if (mimeType == null) {        
            mimeType = "application/octet-stream";
        }

        response.setContentType(mimeType);
        response.setContentLength((int) downloadFile.length());

        String headerKey = "Content-Disposition";
        String headerValue = String.format("attachment; filename=\"%s\"", downloadFile.getName());
        response.setHeader(headerKey, headerValue);

        OutputStream outStream = response.getOutputStream();

        byte[] buffer = new byte[4096];
        int bytesRead = -1;

        while ((bytesRead = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, bytesRead);
        }

        inStream.close();
        outStream.close();     
    }
}
1 resposta
solução!

Oi Ricardo,

Para evitar problemas de memória, no caso de arquivos grandes, o ideal é ir fazendo flush de poucos em poucos bytes. Algo como:

byte[] buf = new byte[4096];
int bytesread = 0;
int bytesBuffered = 0;
while ((bytesread = is.read(buf)) > -1) {
    os.write(buf, 0, bytesread);
    bytesBuffered += bytesread;

    //flush a cada 1MB
    if (bytesBuffered > 1024 * 1024) { 
        bytesBuffered = 0;
        os.flush();
    }
}
if (os != null) {
    os.flush();
}

Você pode também extrair essa lógica de download para uma classe separada, para facilitar o reaproveitamento e organizar o código:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    new ServletFileDownloader().download("C:/Test/Download/MYPIC.JPG", response);
}

public class ServletFileDownloader {

    public void download(String filePath, HttpServletResponse response) {
        try {
            File downloadFile = new File(filePath);
            addResponseInfo(response, downloadFile);

            FileInputStream is = new FileInputStream(downloadFile);
            OutputStream os = response.getOutputStream();
            executeDownload(is, os);
            is.close();
            os.close();
        } catch (IOException e) {
            throw new RuntimeException("erro ao efetuar o download do arquivo!", e);
        }
    }

    private void executeDownload(FileInputStream is, OutputStream os) throws IOException {
        byte[] buf = new byte[4096];
        int bytesread = 0;
        int bytesBuffered = 0;
        while ((bytesread = is.read(buf)) > -1) {
            os.write(buf, 0, bytesread);
            bytesBuffered += bytesread;

            //flush a cada 1MB
            if (bytesBuffered > 1024 * 1024) { 
                bytesBuffered = 0;
                os.flush();
            }
        }
        if (os != null) {
            os.flush();
        }
    }

    private void addResponseInfo(HttpServletResponse response, File file) throws IOException {
        response.setContentType(getFileMimeType(file));
        response.setContentLength((int) file.length());

        String headerKey = "Content-Disposition";
        String headerValue = String.format("attachment; filename=\"%s\"", file.getName());
        response.setHeader(headerKey, headerValue);
    }

    private String getFileMimeType(File file) throws IOException {
        String mimeType = Files.probeContentType(file.toPath());
        if (mimeType == null) {
            mimeType = "application/octet-stream";
        }
        return mimeType;
    }

}

Bons estudos!

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software