13

I am able to download a single file but how I can download a zip file which contain multiple files.

Below is the code to download a single file but I have multiples files to download. Any help would greatly appreciated as I am stuck on this for last 2 days.

@GET
@Path("/download/{fname}/{ext}")
    @Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response  downloadFile(@PathParam("fname") String fileName,@PathParam("ext") String fileExt){
    File file = new File("C:/temp/"+fileName+"."+fileExt);
    ResponseBuilder rb = Response.ok(file);
    rb.header("Content-Disposition", "attachment; filename=" + file.getName());
    Response response = rb.build();
    return response;
}
abhishek chauhan
  • 367
  • 2
  • 6
  • 15
  • have you tried something with `response.setContentType("application/zip");`? – Shreyas Jul 14 '18 at 20:21
  • I think you will have to use `rb.header("Content-Type", "application/zip");` – Shreyas Jul 14 '18 at 20:24
  • So zip file is already there or you wish to create it on the fly? Also, one concern if full file needs to be in memory or needs to be streamed in chunks while downloading ? – Sabir Khan Jul 16 '18 at 12:39
  • I have to create the zip file on fly and to answer your second question is that full file needs to be in memory but we are restricting the download if it goes above 100 MB. – abhishek chauhan Jul 17 '18 at 10:04
  • @abhishekchauhan: Its not guaranteed that you will have a single concurrent client so as per memory foot print , 100MB might have to get multiplied by number of concurrent allowed connections. Just a thought. – Sabir Khan Jul 17 '18 at 12:33

3 Answers3

13

Here is my working code I have used response.getOuptStream()

@RestController
public class DownloadFileController {

    @Autowired
    DownloadService service;

    @GetMapping("/downloadZip")
    public void downloadFile(HttpServletResponse response) {

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment;filename=download.zip");
        response.setStatus(HttpServletResponse.SC_OK);

        List<String> fileNames = service.getFileName();

        System.out.println("############# file size ###########" + fileNames.size());

        try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {
            for (String file : fileNames) {
                FileSystemResource resource = new FileSystemResource(file);

                ZipEntry e = new ZipEntry(resource.getFilename());
                // Configure the zip entry, the properties of the file
                e.setSize(resource.contentLength());
                e.setTime(System.currentTimeMillis());
                // etc.
                zippedOut.putNextEntry(e);
                // And the content of the resource:
                StreamUtils.copy(resource.getInputStream(), zippedOut);
                zippedOut.closeEntry();
            }
            zippedOut.finish();
        } catch (Exception e) {
            // Exception handling goes here
        }
    }
}

Service Class:-

public class DownloadServiceImpl implements DownloadService {

    @Autowired
    DownloadServiceDao repo;

    @Override
    public List<String> getFileName() {

        String[] fileName = { "C:\\neon\\FileTest\\File1.xlsx", "C:\\neon\\FileTest\\File2.xlsx", "C:\\neon\\FileTest\\File3.xlsx" };

        List<String> fileList = new ArrayList<>(Arrays.asList(fileName));       
        return fileList;
    }
}
akokskis
  • 1,486
  • 15
  • 32
abhishek chauhan
  • 367
  • 2
  • 6
  • 15
4

Use these Spring MVC provided abstractions to avoid loading of whole file in memory. org.springframework.core.io.Resource & org.springframework.core.io.InputStreamSource

This way, your underlying implementation can change without changing controller interface & also your downloads would be streamed byte by byte.

See accepted answer here which is basically using org.springframework.core.io.FileSystemResource to create a Resource and there is a logic to create zip file on the fly too.

That above answer has return type as void, while you should directly return a Resource or ResponseEntity<Resource> .

As demonstrated in this answer, loop around your actual files and put in zip stream. Have a look at produces and content-type headers.

Combine these two answers to get what you are trying to achieve.

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
0
public void downloadSupportBundle(HttpServletResponse response){

      File file = new File("supportbundle.tar.gz");
      Path path = Paths.get(file.getAbsolutePath());
      logger.debug("__path {} - absolute Path{}", path.getFileName(),
            path.getRoot().toAbsolutePath());

      response.setContentType("application/octet-stream");
      response.setHeader("Content-Disposition", "attachment;filename=supportbundle.tar.gz");
      response.setStatus(HttpServletResponse.SC_OK);

      System.out.println("############# file name ###########" + file.getName());

      try (ZipOutputStream zippedOut = new ZipOutputStream(response.getOutputStream())) {

         FileSystemResource resource = new FileSystemResource(file);

         ZipEntry e = new ZipEntry(resource.getFilename());
         e.setSize(resource.contentLength());
         e.setTime(System.currentTimeMillis());
         zippedOut.putNextEntry(e);
         StreamUtils.copy(resource.getInputStream(), zippedOut);
         zippedOut.closeEntry();

         zippedOut.finish();
      } catch (Exception e) {
         
      }
}
David Buck
  • 3,752
  • 35
  • 31
  • 35