programing

Spring REST - ZIP 파일을 생성하여 클라이언트로 전송

linuxpc 2023. 9. 3. 12:12
반응형

Spring REST - ZIP 파일을 생성하여 클라이언트로 전송

백엔드에서 받은 아카이브된 파일이 들어 있는 ZIP 파일을 만든 다음 이 파일을 사용자에게 보냅니다.이틀째 답을 찾고 있는데 제대로 된 해결책을 찾을 수가 없어요, 아마 당신이 도와주실 수 있을 거예요 :)

현재 코드는 다음과 같습니다(Spring 컨트롤러에서 모든 작업을 수행해서는 안 된다는 것을 알고 있지만, 신경 쓰지 마십시오. 테스트 목적일 뿐입니다. 작동하는 방법을 찾기 위한 것입니다.).

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

        // Creating byteArray stream, make it bufferable and passing this buffer 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파일 등.

Windows에서 .zip 파일이 손상되어 제가 말한 대로 열리지도 못했습니다.

제 생각에도, 그 제거는.html자동으로 확장하는 것이 가장 좋은 옵션이 될 것입니다. 하지만 어떻게 해야 할까요?

보이는 것만큼 어렵지 않기를 바랍니다 :)
감사해요.

문제가 해결되었습니다.

대체됨:

response.setContentType("application/zip");

포함:

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

그리고 이제 난 맑고 아름다운 투명한.zip파일.


만약 여러분 중에 더 나은 혹은 더 빠른 제안이 있거나 조언을 하고 싶은 사람이 있다면, 저는 궁금해요.

@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();
}
@RequestMapping(value="/zip", produces="application/zip")
public ResponseEntity<StreamingResponseBody> zipFiles() {
    return ResponseEntity
            .ok()
            .header("Content-Disposition", "attachment; filename=\"test.zip\"")
            .body(out -> {
                var zipOutputStream = new ZipOutputStream(out);

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

사용 중REST Web ServiceSpring Boot그리고 나는 엔드포인트가 항상 되돌아오도록 설계했다.ResponseEntity그것이 그렇든JSON또는PDF또는ZIP그리고 저는 부분적으로 영감을 받은 다음과 같은 해결책을 생각해냈습니다.denov's answer이 질문과 내가 변환하는 방법을 배운 다른 질문에서.ZipOutputStream안으로byte[]그것을 먹이기 위해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실용적인 방법에서.

Spring Boot 응용 프로그램에서 작동한 유일한 기능(하드코딩된 파일 경로 없음!)

@GetMapping(value = "/zip-download", produces="application/zip")
public void zipDownload(@RequestParam List<String> name, HttpServletResponse response) throws IOException {

    response.setStatus(HttpServletResponse.SC_OK);
    response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + zipFileName + "\"");

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

    for (String fileName : name) {

        // resource content length
        int contentLength = 123;
        // resource input stream
        InputStream stream = InputStream.nullInputStream();

        ZipEntry zipEntry = new ZipEntry(fileName);
        zipEntry.setSize(contentLength);

        zipOut.putNextEntry(zipEntry);
        StreamUtils.copy(stream, zipOut);

        zipOut.closeEntry();
    }

    zipOut.finish();
    zipOut.close();
}

언급URL : https://stackoverflow.com/questions/27952949/spring-rest-create-zip-file-and-send-it-to-the-client

반응형