简体   繁体   中英

Slow concurrent file download with Java Spring and Tomcat

I have developed a Spring MVC project providing REST. One of the methods returns a zip file of data. If the data packet is small (under 10MB) or we receive few connections the response time is ok, but if I have a 20MB file or a hundred of requests, it is slow. This is my code:

@RequestMapping(value = "{venue}/packet", method = RequestMethod.GET)
public ResponseEntity getPackage(@PathVariable("venue") int id,
        @RequestParam(value = "reqid", required = false) int reqid,
        @RequestParam(value = "version", required = false, defaultValue = "1.0") String version,
        @ModelAttribute("user") Developer user, HttpServletRequest request, HttpServletResponse resp) {

    String req_code = "[" + Thread.currentThread().getId() + "] ";
    long start = System.currentTimeMillis();

    // Check if the version of the client is already updated
    if (!version.equals(venue.getVersion())) {

        //Returns a file from File System
        File zipped = venueService.getPacket(venue);
        resp.setContentType("application/zip");
        resp.setContentLength((int) zipped.length());
        resp.setHeader("Content-Disposition", "attachment;filename=" + zipped.getName());

        try {

        InputStream is = new BufferedInputStream(new FileInputStream(zipped));
        long copytime = System.currentTimeMillis();
        packet_log.info(req_code + "Start copy req:" + reqid);

        IOUtils.copy(is, resp.getOutputStream());
        resp.flushBuffer();
        is.close();
        packet_log.info(req_code + "End copy req:" + reqid + " in " + (System.currentTimeMillis() - copytime));

        } catch (IOException e) {
        e.printStackTrace();
        }
        long dur = System.currentTimeMillis() - start;
        packet_log.info(req_code + "Send to client req:" + reqid + " in " + dur / 1000);
        packet_log.info("");
        return new ResponseEntity<Object>("", headers, HttpStatus.OK);
    }

I have inserted a lot of logs and a "reqid" parameter, shared by client and server, to monitor every request. When I have multiple concurrent request the command IOUtils.copy(is, resp.getOutputStream()); become very slow. If I send 80 requests it takes even more then a minute to copy a 20MB file.

I don't think there is a problem on my code. Maybe it depends on how Spring MVC manages @RestController beans? Or is a I/O problem of access to disk? The system is on a AWS EC2 virtual machine.

What you have implemented is a Blocking way to download files by Directly writing to the HttpServletResponse 's OutputStream .

If you are using Spring 4.2+ you can return a StreamingResponseBody which will Asynchronously stream the File bytes to the receiving client.

Have a look at this post and this post for more information

Instead of using IOUtils.copy(is, resp.getOutputStream()); to copy input stream of file to HttpServletResponse 's output stream use FileSystemResource to dowload your zip file at client end.

public FileSystemResource downloadFile(HttpServletResponse response) {
    File file = new File("BASE_PATH/MY_ZIP_FILE.zip");
    response.setContentType("application/zip");      
    response.setHeader("Content-Disposition", "attachment; filename="+"MY_ZIP_FILE.zip");
    return new FileSystemResource(file); 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM