Подтвердить что ты не робот

Spring REST - создать .zip файл и отправить его клиенту

Я хочу создать ZIP файл, содержащий мои zipped файлы, которые я получаю из бэкэнд, а затем отправить этот файл пользователю. В течение 2 дней я искал ответ и не мог найти правильное решение, может быть, вы можете мне помочь:)

На данный момент код выглядит следующим образом: (я знаю, что я не должен делать все это в контроллере spring, но не заботясь об этом, он просто предназначен для тестирования, чтобы найти способ заставить его работать)

    @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();
    }

Но проблема в том, что с использованием кода, когда я ввожу URL: localhost: 8080/zip Я получаю файл: test.zip.html вместо zip файла

Когда я удаляю расширение .html и оставляю только test.zip, он правильно открывается, чтобы избежать возврата этого расширения .html? почему он добавлен?

Я понятия не имею, что еще я могу сделать. Я также пытался заменить ByteArrayOuputStream примерно следующим образом:

OutputStream outputStream = response.getOutputStream();

и установите метод void, чтобы он ничего не возвращал, но создал .zip файл, который был поврежден?

На моем macbook после распаковки test.zip я получал test.zip.cpgz, который снова давал мне файл test.zip и т.д.

В окнах файл .zip был поврежден, как я сказал, и даже не смог его открыть.

Я также предполагаю, что удаление расширения .html автоматически будет лучшим вариантом, но как? Надеюсь, что это не так сложно, как кажется:) спасибо

4b9b3361

Ответ 1

похоже, решается. Я заменил:

response.setContentType("application/zip");

с:

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

И теперь я получаю ясный, красивый .zip файл:)

Если у кого-то из вас есть лучшее или более быстрое предложение или просто хотите дать какой-то совет, тогда вперед, мне любопытно.

Ответ 2

@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();
}

Ответ 3

Я использую REST Web Service Spring Boot и я разработал конечные точки, чтобы всегда возвращать ResponseEntity будь то JSON или PDF или ZIP и я придумал следующее решение, которое частично вдохновлено denov answer в этом вопросе, а также другим вопросом где я узнал, как преобразовать ZipOutputStream в byte[] чтобы ZipOutputStream его ResponseEntity качестве вывода конечной точки.

Во всяком случае, я создал простой класс утилиты с двумя методами для загрузки файлов в pdf и 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); 
    }
}

И теперь конечная точка может легко вернуть ResponseEntity<?> Как показано ниже, используя данные byte[] и пользовательские заголовки, специально предназначенные для pdf или 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); 
}

BinaryOutputWrapper - это простой неизменный POJO класс, который я создал с помощью private byte[] data; и org.springframework.http.HttpHeaders headers; как поля, чтобы возвращать как data и headers из метода полезности.