web-dev-qa-db-fra.com

Printemps REST - créer un fichier .Zip et l'envoyer au client

Je veux créer un fichier .zip contenant les fichiers compressés que je reçois du backend, puis envoyer ce fichier à l'utilisateur. Cela fait 2 jours que je cherche la réponse et que je ne trouve pas la solution appropriée, peut-être que tu peux m'aider :) 

Pour l'instant, le code est comme ceci: (Je sais que je ne devrais pas tout faire dans le contrôleur de ressort, mais ne vous inquiétez pas, c'est juste pour des tests, pour trouver le moyen de le faire fonctionner)

    @RequestMapping(value = "/Zip")
    public byte[] zipFiles(HttpServletResponse response) throws IOException{
        //setting headers
        response.setContentType("application/Zip");
        response.setStatus(HttpServletResponse.SC_OK);
        response.addHeader("Content-Disposition", "attachment; filename=\"test.Zip\"");

        //creating byteArray stream, make it bufforable and passing this buffor to ZipOutputStream
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
        ZipOutputStream zipOutputStream = new ZipOutputStream(bufferedOutputStream);

        //simple file list, just for tests
        ArrayList<File> files = new ArrayList<>(2);
        files.add(new File("README.md"));

        //packing files
        for (File file : files) {
            //new Zip entry and copying inputstream with file to zipOutputStream, after all closing streams
            zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
            FileInputStream fileInputStream = new FileInputStream(file);

            IOUtils.copy(fileInputStream, zipOutputStream);

            fileInputStream.close();
            zipOutputStream.closeEntry();
        }

        if (zipOutputStream != null) {
            zipOutputStream.finish();
            zipOutputStream.flush();
            IOUtils.closeQuietly(zipOutputStream);
        }
        IOUtils.closeQuietly(bufferedOutputStream);
        IOUtils.closeQuietly(byteArrayOutputStream);
        return byteArrayOutputStream.toByteArray();
    }

Mais le problème est que, en utilisant le code, lorsque je saisis l'URL: localhost: 8080/Zip, je reçois le fichier: test.Zip.html au lieu du fichier .Zip.

Lorsque je supprime l'extension .html et que je laisse juste test.Zip, il s'ouvre correctement comment éviter de renvoyer cette extension .html? pourquoi est-ce ajouté?

Je n'ai aucune idée de ce que je peux faire d'autre. J'essayais également de remplacer ByteArrayOuputStream par quelque chose comme:

OutputStream outputStream = response.getOutputStream();

et définissez la méthode sur void donc il ne retourne rien, mais il a créé un fichier .Zip qui a été endommagé. 

Sur mon macbook après avoir décompressé le fichier test.Zip je recevais test.Zip.cpgz qui me donnait à nouveau le fichier test.Zip et ainsi de suite.

Sous Windows, le fichier .Zip était endommagé, comme je l’ai dit, et ne pouvait même pas l’ouvrir. 

Je suppose aussi que la suppression de l'extension .html sera automatiquement la meilleure option, mais comment? J'espère que ce n'est pas aussi difficile que cela semble être :) merci

26
azalut

semble être résolu. J'ai remplacé:

response.setContentType("application/Zip");

avec:

@RequestMapping(value = "/Zip", produces="application/Zip")

Et maintenant, je suis clair, beau fichier .Zip :)

Si certains d'entre vous ont une proposition meilleure ou plus rapide, ou veulent juste donner un conseil, alors allez-y, je suis curieux.

25
azalut
@RequestMapping(value="/Zip", produces="application/Zip")
public void zipFiles(HttpServletResponse response) throws IOException {

    //setting headers  
    response.setStatus(HttpServletResponse.SC_OK);
    response.addHeader("Content-Disposition", "attachment; filename=\"test.Zip\"");

    ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());

    // create a list to add files to be zipped
    ArrayList<File> files = new ArrayList<>(2);
    files.add(new File("README.md"));

    // package files
    for (File file : files) {
        //new Zip entry and copying inputstream with file to zipOutputStream, after all closing streams
        zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
        FileInputStream fileInputStream = new FileInputStream(file);

        IOUtils.copy(fileInputStream, zipOutputStream);

        fileInputStream.close();
        zipOutputStream.closeEntry();
    }    

    zipOutputStream.close();
}
18
denov

J'utilise REST Web Service de Spring Boot et j'ai conçu les systèmes d'extrémité pour toujours renvoyer ResponseEntity qu'il s'agisse de JSON ou PDF ou Zip et j'ai proposé la solution suivante partiellement inspirée de denov's answer dans cette question ainsi que d'une autre question où j'ai appris à convertir ZipOutputStream en byte[] afin de le transmettre à ResponseEntity en tant que sortie du noeud final. 

Quoi qu’il en soit, j’ai créé une classe d’utilitaire simple avec deux méthodes de téléchargement de fichier pdf et Zip 

@Component
public class FileUtil {
    public BinaryOutputWrapper prepDownloadAsPDF(String filename) throws IOException {
        Path fileLocation = Paths.get(filename);
        byte[] data = Files.readAllBytes(fileLocation);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/pdf"));
        String outputFilename = "output.pdf";
        headers.setContentDispositionFormData(outputFilename, outputFilename);
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");

        return new BinaryOutputWrapper(data, headers); 
    }

    public BinaryOutputWrapper prepDownloadAsZIP(List<String> filenames) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/Zip"));
        String outputFilename = "output.Zip";
        headers.setContentDispositionFormData(outputFilename, outputFilename);
        headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");

        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(byteOutputStream);

        for(String filename: filenames) {
            File file = new File(filename); 
            zipOutputStream.putNextEntry(new ZipEntry(filename));           
            FileInputStream fileInputStream = new FileInputStream(file);
            IOUtils.copy(fileInputStream, zipOutputStream);
            fileInputStream.close();
            zipOutputStream.closeEntry();
        }           
        zipOutputStream.close();
        return new BinaryOutputWrapper(byteOutputStream.toByteArray(), headers); 
    }
}

Et maintenant, le noeud final peut facilement renvoyer ResponseEntity<?>, comme indiqué ci-dessous, à l'aide des données byte[] et des en-têtes personnalisés spécifiquement adaptés à pdf ou Zip

@GetMapping("/somepath/pdf")
public ResponseEntity<?> generatePDF() {
    BinaryOutputWrapper output = new BinaryOutputWrapper(); 
    try {
        String inputFile = "sample.pdf"; 
        output = fileUtil.prepDownloadAsPDF(inputFile);
        //or invoke prepDownloadAsZIP(...) with a list of filenames
    } catch (IOException e) {
        e.printStackTrace();
        //Do something when exception is thrown
    } 
    return new ResponseEntity<>(output.getData(), output.getHeaders(), HttpStatus.OK); 
}

La BinaryOutputWrapper est une simple classe POJO immuable que j'ai créée avec private byte[] data; et org.springframework.http.HttpHeaders headers; comme champs afin de renvoyer à la fois data et headers à partir de la méthode d'utilitaire. 

1
Raf