Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download Multiple files Java Spring

I am trying to download multiple file with one http get request in my spring-mvc application.

I have looked at other posts, saying you could just zip the file and send this file but it's not ideal in my case, as the file are not in direct access from the application. To get the files I have to query a REST interface, which streams the file from either hbase or hadoop.

I can have files bigger than 1 Go, so downloading the files into a repository, zipping them and sending them to the client would be too long. (Considering that the big file are already zip, zipping won't compress them).

I saw here and there that you can use multipart-response to download multiple files at once, but I can't get any result. Here is my code:

String boundaryTxt = "--AMZ90RFX875LKMFasdf09DDFF3";
response.setContentType("multipart/x-mixed-replace;boundary=" + boundaryTxt.substring(2));
ServletOutputStream out = response.getOutputStream();
        
// write the first boundary
out.write(("\r\n"+boundaryTxt+"\r\n").getBytes());

String contentType = "Content-type: application/octet-stream\n";
        
for (String s:files){
    System.out.println(s);
    String[] split = s.trim().split("/");
    db = split[1];
    key = split[2]+"/"+split[3]+"/"+split[4];
    filename = split[4];
            
    out.write((contentType + "\r\n").getBytes());
    out.write(("\r\nContent-Disposition: attachment; filename=" +filename+"\r\n").getBytes());
    
    InputStream is = null;
    if (db.equals("hadoop")){
        is = HadoopUtils.get(key);
    }
    else if (db.equals("hbase")){
        is = HbaseUtils.get(key);
    }
    else{
        System.out.println("Wrong db with name: " + db);
    }
    byte[] buffer = new byte[9000]; // max 8kB for http get
    int data;
    while((data = is.read(buffer)) != -1) { 
        out.write(buffer, 0, data);
    } 
    is.close(); 
       
    // write bndry after data
    out.write(("\r\n"+boundaryTxt+"\r\n").getBytes());
    response.flushBuffer();
    }
// write the ending boundary
out.write((boundaryTxt + "--\r\n").getBytes());
response.flushBuffer();
out.close();
}   

The weird part is that I get different result depending on the navigator. Nothing happends in Chrome (looked at the console) and in Firefox, I got a prompt asking to download for each file but it doesn't have the right type nor the right name (nothing in console either).

Is there any bug in my code? If no, is there any alternative?

Edit

I also saw this post: Unable to send a multipart/mixed request to spring MVC based REST service

Edit 2

firefox result

The content of this file is what I want, but why can't I get the right name and why can't chrome download anything?

like image 208
Whitefret Avatar asked Apr 27 '16 09:04

Whitefret


1 Answers

This is the way you can do the download via zip :

try {
      List<GroupAttachments> groupAttachmentsList = attachIdList.stream().map(this::getAttachmentObjectOnlyById).collect(Collectors.toList()); // Get list of Attachment objects
            Person person = this.personService.getCurrentlyAuthenticatedUser();
            String zipSavedAt = zipLocation + String.valueOf(new BigInteger(130, random).toString(32)); // File saved location
            byte[] buffer = new byte[1024];
            FileOutputStream fos = new FileOutputStream(zipSavedAt);
            ZipOutputStream zos = new ZipOutputStream(fos);

                GroupAttachments attachments = getAttachmentObjectOnlyById(attachIdList.get(0));

                    for (GroupAttachments groupAttachments : groupAttachmentsList) {
                            Path path = Paths.get(msg + groupAttachments.getGroupId() + "/" +
                                    groupAttachments.getFileIdentifier());   // Get the file from server from given path
                            File file = path.toFile();
                            FileInputStream fis = new FileInputStream(file);
                            zos.putNextEntry(new ZipEntry(groupAttachments.getFileName()));
                            int length;

                            while ((length = fis.read(buffer)) > 0) {
                                zos.write(buffer, 0, length);
                            }
                            zos.closeEntry();
                            fis.close();

                    zos.close();
                    return zipSavedAt;
            }
        } catch (Exception ignored) {
        }
        return null;
    }

Controller method for downloading zip :

 @RequestMapping(value = "/URL/{mode}/{token}")
    public void downloadZip(HttpServletResponse response, @PathVariable("token") String token,
                            @PathVariable("mode") boolean mode) {
        response.setContentType("application/octet-stream");
        try {
            Person person = this.personService.getCurrentlyAuthenticatedUser();
            List<Integer> integerList = new ArrayList<>();
            String[] integerArray = token.split(":");
            for (String value : integerArray) {
                integerList.add(Integer.valueOf(value));
            }
            if (!mode) {
                String zipPath = this.groupAttachmentsService.downloadAttachmentsAsZip(integerList);
                File file = new File(zipPath);
                response.setHeader("Content-Length", String.valueOf(file.length()));
                response.setHeader("Content-Disposition", "attachment; filename=\"" + person.getFirstName() + ".zip" + "\"");
                InputStream is = new FileInputStream(file);
                FileCopyUtils.copy(IOUtils.toByteArray(is), response.getOutputStream());
                response.flushBuffer();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Have fun, incase of doubt, lemme know.

Update

Byte-array in a ZIP file. You can use this code in a loop as in the first method I gave :

public static byte[] zipBytes(String filename, byte[] input) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ZipOutputStream zos = new ZipOutputStream(baos);
    ZipEntry entry = new ZipEntry(filename);
    entry.setSize(input.length);
    zos.putNextEntry(entry);
    zos.write(input);
    zos.closeEntry();
    zos.close();
    return baos.toByteArray();
}
like image 179
We are Borg Avatar answered Sep 27 '22 17:09

We are Borg